欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript事件-事件冒泡,事件捕获,事件绑定与解绑,事件委托

程序员文章站 2022-05-06 07:58:03
...

一、事件

HTML 事件可以是浏览器行为,也可以是用户行为。
通常,在事件触发时 JavaScript 可以执行一些代码。

二、DOM事件三个阶段

当一个DOM事件被触发时,它不仅仅只是单纯地在本身对象上触发一次,而是会经历三个不同的阶段:

  • 捕获阶段:先由文档的根节点document往事件触发对象,从外向内捕获事件对象;
  • 目标阶段:到达目标事件位置(事发地),触发事件;
  • 冒泡阶段:再从目标事件位置往文档的根节点方向回溯,从内向外冒泡事件对象。

    冒泡型事件流
    

    JavaScript事件-事件冒泡,事件捕获,事件绑定与解绑,事件委托

    捕获型事件流
    

    JavaScript事件-事件冒泡,事件捕获,事件绑定与解绑,事件委托

  • .W3C标准模型

    "DOM2事件"规范要求的事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段
    

    JavaScript事件-事件冒泡,事件捕获,事件绑定与解绑,事件委托
    W3C标准则采用捕获+冒泡
    W3C事件模型中发生的任何事件,先从其祖先元素window开始一路向下捕获, 直到达到目标元素,其后再次从目标元素开始冒泡。
    而作为开发者, 可以决定事件处理器是注册在捕获或者是冒泡阶段。如果addEventListener的最后一个参数是true, 那么处理函数将在捕获阶段被触发; 否则(false), 会在冒泡阶段被触发!默认false!

三、事件绑定与解绑

总结:
当使用on或者addEventListener绑定事件的时候,如果绑定同一个事件同一个方法,只会执行一次,但是attachEvent会多次绑定。在jquery中,无论是on添加事件,还是直接加事件函数,同一个事件都可以多次绑定

1.直接在HTML中事件处理(不推荐)
<input type="button" value="showClick" onclick="showClick()" />
2.直接在dom对象上注册事件名称,就是DOM0写法,所有浏览器支持

如果handle是同一个方法,只执行一次。

//事件绑定
document.getElementById("box").onclick = handle;/
document.getElementById("box")["onmousemover"] = handle;

//事件解绑
document.getElementById("box")["onmousemover"] = null;

//阻止默认事件(默认事件行为:href=""链接,submit表单提交等)
document.getElementById("box").onclick = function() {
    ……                         //你的代码
    return false;              //通过返回false值阻止默认事件行为
};

注释:
return false 的含义不是阻止事件继续向顶层元素传播,而是阻止浏览器对事件的默认处理。
return false 只在当前函数有效,不会影响其他外部函数的执行。
总结:
retrun true; 返回正确的处理结果。
return false;返回错误的处理结果,终止处理;阻止提交表单;阻止执行默认的行为。
return;把控制权返回给页面。

3.DOM2事件模型
  • DOM2支持同一dom元素注册多个同种事件。
  • DOM2新增了捕获和冒泡的概念。
(1)addEventListener(event.type, handle, boolean); IE8及以下不支持

事件类型没有on,第三个参数false,表示在事件第三阶段(冒泡)触发,true表示在事件第一阶段(捕获)触发。
如果handle是同一个方法,只执行一次。

var element=document.getElementById("box");
var handler=function(){
     event.preventDefault( );      //阻止默认事件
}
//绑定事件
element.addEventListener('click', handler, false);  
//解绑事件
element.removeEventListener('click', handle, false);
(2)attachEvent(event.type, handle ); IE特有,兼容IE8及以下,可添加多个事件处理程序,只支持冒泡阶段,并不属于DOM2

如果handle是同一个方法,绑定几次执行几次,这点和addEventListener不同。事件类型要加on,例如onclick而不是click

特别注意:

在 IE 中使用 attachEvent() 与DOM0和DOM2addEventListener有一主要区别:事件处理程序的作用域。在这些方法中,事件处理程序会在其所属元素的作用域内运行;在使用 attachEvent() 方法的情况下,事件处理程序会在全局作用域中运行,因此 this 等于 window。

var element=document.getElementById("box");
var handler=function(){
    event.returnValue = false;       //阻止默认事件
}
/绑定事件
element.attachEvent('onclick', handler); 
//解绑事件,参数和绑定一样
element.detachEvent("onclick", handler);
(3)封装事件绑定与解绑函数,兼容浏览器
// 事件绑定
function addEvent(element, eType, handler, bol) {
    var boll = bol | false;
    if(element.addEventListener){           //如果支持addEventListener
        element.addEventListener(eType, handler, boll );
    }else if(element.attachEvent){          //如果支持attachEvent
        element.attachEvent("on"+eType, handler);
    }else{                                  //否则使用兼容的onclick绑定
        element["on"+eType] = handle;
    }
}
// 事件解绑
function removeEvent(element, eType, handler, bol) {
    var boll = bol | false;
    if(element.addEventListener){
        element.removeEventListener(eType, handler, boll );
    }else if(element.attachEvent){
        element.detachEvent("on"+eType, handler);
    }else{
        element["on"+eType] = null;
    }
}

我们可以将两个函数写成函数的方法模式

//用事件冒泡方式,如果想兼容事件捕获只需要添加个bool参数
var EventUtil = {
    addEvent: function(element,type,handler) {
        if (element.addEventListener) {
            element.addEventListener(type,handler,false);
        }
        else if (element.attachEvent) {
            element.attachEvent('on'+type,handler);
        }
        else {
            element['on'+type] = handler;
        }
    },

    removeEvent: function(element,type,handler) {
        if (element.removeEventListener)
        {
            element.removeEventListener(type,handler,false);
        }
        else if(element.detachEvent) {
            element.detachEvent('on' +type,handler);
        }
        else {
            element['on'+type] = null;
        }
    }
}

//使用形式

var box= document.getElementById("box");
var handler = function(){
    console.log(111);
};
EventUtil.addEvent(box, "click", handler);
EventUtil.removeEvent(box, "click", handler);

四、事件冒泡–主要在于理解透彻,才能在实际中运用

  • 从事件源,自下而上的过程中,阻止向上冒泡,即阻止触发上级元素的事件触发
  • 也可以理解为,上级元素做事件触发时,在此事件源上无效
<div id="box">
    <span id="spa">子子元素</span>
</div>

document.getElementById('spa').addEventListener('click',function(e){
    console.log('spa')
    var e = event||window.event;
    e.stopPropagation()
    //在子级及以下的一个元素上进行阻止冒泡,在这块区域不会触发父级及以上的相同事件
})
document.getElementById('box').addEventListener('click',function(){
    console.log('box')
})
  • 在vue中,为了防止click事件冒泡,防止点击子级部分触发父级事件,会在子级直接加一个@click.stop

阻止事件冒泡的方法:

function stopPropagation(event){
    event=window.event||event;
    if(event.stopPropagation){ 
      event.stopPropagation();
    }else{
     event.cancelBubble=true;
    }
}

五、事件委托

一般有两种情况会用到事件委托

  • 新增的子元素是没有事件的,说明添加子节点的时候,事件没有一起添加进去
  • 子元素太多,一个个循环遍历添加事件耗费性能

使用事件委托的原理:

  • 利用事件冒泡的特性,将里层的事件委托给外层事件,根据event对象的属性进行事件委托,改善性能。
  • 使用事件委托能够避免对特定的每个节点添加事件监听器;事件监听器是被添加到它们的父元素上。
  • 事件监听器会分析从子元素冒泡上来的事件,找到是哪个子元素的事件。
<ul id="J_List">
     <li><div class="id">1</div></li>
     <li><div class="id">2</div></li>
     <li><div class="user-delete">3</div></li>
 </ul>

var lis = document.querySelector('#J_List');
lis.addEventListener('click', function(e){
    stopPropagation(e)
    var target = e.target || e.srcElement;
    //在这里处理子元素的事件
    if (target && target.className.toLowerCase()==='user-delete') {
        target.parentNode.parentNode.removeChild(target.parentNode);
    }
})

在jquery中经常用到的,即事件委托

$('body').on('click','.box1',function(){})

六、最后给大家送上一个跨浏览器的事件对象

var EventUtil={
    getEvent:function(event){
        return event||window.event;
    },
    getTarget:function(event){
        return event.target||event.srcElement;
    },
    preventDefault:function(){
        if(event.preventDefault){
            event.preventDefault();
        }else{
            event.returnValue=false;
        }
    },
    stopPropagation:function(){
        if(event.stopPropagation){
            event.stopPropagation();
        }else{
            event.cancelBubble=true;
        }
    },
    addEvent:function(element,type,handler){
        if(element.addEventListener){
            element.addEventListener(type,handler,false);
        }else if(element.attachEvent){
             element["e"+type]=function(){
              handler.call(element)
          }
            element.attachEvent("on"+type,element["e"+type]);
        }else{
            element["on"+type]=handler;
        }
    },
    removeEvent:function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);
        }else if(element.detachEvent){
            element.detachEvent("on"+type,element["e"+type]);
            element["e"+type]=null;    
        }else{
            element["on"+type]=null;
        }
    }

  };