文章目录
  1. 1. 事件冒泡
    1. 1.1. 注意
  2. 2. 事件捕获
  3. 3. W3C DOM事件流
    1. 3.1. 独特性质
  4. 4. 事件参数
  5. 5. 事件绑定、移除
    1. 5.1. 整合通用版
  6. 6. 事件冒泡优点
  7. 7. 事件冒泡缺点
  8. 8. 阻止事件冒泡
    1. 8.1. 通用版
  9. 9. JQuery阻止事件冒泡
    1. 9.1. preventDefault()

在前端开发过程中我们经常会遇到给页面元素添加事件的问题,不同的浏览器的事件监听机制也不尽相同,大家可能听过事件冒泡和事件捕获这些概念,今天就简单介绍一下JS事件的监听机制。

事件冒泡

当一个元素上的事件被触发的时候,比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡;这个事件从原始元素开始一直冒泡到DOM树的最上层(一般是window,document)。

IE 5.5: div -> body -> document

IE 6.0: div -> body -> html -> document

Mozilla 1.0: div -> body -> html -> document -> window

我们使用的所有浏览器都支持事件冒泡。

早期微软IE浏览器使用冒泡事件流。

注意

  1. 不是所有的事件都能冒泡。以下事件不冒泡:blur、focus、load、unload
  2. 火狐等标准浏览器(DOM事件流)是在document到body这几层里,从外到里(outside→inside)的捕获型事件的传播;body以内的,是冒泡型传播

事件捕获

捕获阶段是一个和冒泡阶段完全相反的过程,即事件由祖先元素向子元素传播。但是,在IE、opera浏览器中是不存在这个阶段的。

早期Netscape浏览器使用捕获事件流。

W3C DOM事件流

同时支持两种事件模型:捕获型事件和冒泡型事件,但是,捕获型事件先发生。两种事件流会触及DOM中的所有对象,从document对象开始,也在document对象结束。

独特性质

  1. 文本节点也触发事件(在IE中不会)

事件参数

在触发dom上的某个事件时,会产生一个事件对象event,浏览器会将一个event对象传入到事件处理程序中(function)

  1. 新式浏览器:默认为事件方法传入一个事件参数对象,而目标元素放在target或event.relatedTarget;
  2. 老式IE浏览器:则不会!而是使用 window.event获取,目标元素放在srcElemtn属性或event.toElement属性中。

新式浏览器也兼容 window.event。所以你会看到如下的代码:

<a id="xc">点击</a>
<script type="text/javascript">
        function handler(e){
                e=e||window.event;
                var _this=e.srcElement||e.target;
                alert(_this.innerHTML);
        }
        var object=document.getElementById('xc');
        object.attachEvent('onclick',handler);
</script>

事件绑定、移除

  1. IE9之前的IE浏览器(事件名要加on : onclick):

    obj.attachEvent(evtype,fn)
    obj.detachEvent(evtype,fn,)
    //evtype是事件类型,带on前缀,fn是事件处理函数
    //在事件处理函数内部,this指向了window,而不是obj
    //解决方法:apply或者call函数;使用事件源代替this
    
  2. 新式浏览器

    obj.addEventListener(evtype,fn,useCapture)
    obj.removeEventListener(evtype,fn,useCapture)
    //evtype是事件类型,不带on前缀,fn是事件处理函数,如果useCapture是true,
    //则事件处理函数在捕获阶段被执行,否则 在冒泡阶段执行。一般使用false
    

整合通用版

function addEvent(obj,evtype,fn,useCapture) {  
    if (obj.addEventListener) {  
        obj.addEventListener(evtype,fn,useCapture);  
    } else {  
        obj.attachEvent("on"+evtype,fn);//IE不支持事件捕获  
    } else {  
        obj["on"+evtype]=fn;//事实上这种情况不会存在  
    }  
}  
function delEvent(obj,evtype,fn,useCapture) {  
    if (obj.removeEventListener) {  
        obj.removeEventListener(evtype,fn,useCapture);  
    } else {  
        obj.detachEvent("on"+evtype,fn);  
    } else {  
        obj["on"+evtype]=null;  
    }  
}  

事件冒泡优点

  1. 事件冒泡允许多个操作被集中处理(把事件处理器添加到一个父级元素上,避免把事件处理器添加到多个子级元素上),它还可以让你在对象层的不同级别捕获事件。
  2. 让不同的对象同时捕获同一事件,并调用自己的专属处理程序做自己的事情,就像老板一下命令,各自员工做自己岗位上的工作去了

那些需要创建的以及驻留在内存中的事件处理器少了,这样我们就提高了性能,并降低了崩溃的风险。

在DOM更新后无须重新绑定事件处理器了。如果你的页面是动态生成的,比如说通过Ajax,你不再需要在元素被载入或者卸载的时候来添加或者删除事件处理器了。

事件冒泡缺点

通常情况下我们都是一步到位,明确自己的事件触发源,并不希望浏览器自作聪明、漫无目的地去帮我们找合适的事件处理程序,即我们明确最精准目标,这种情况下我们不需要事件冒泡。另外通过对事件冒泡的理解,我们知道程序将做多较多额外的事情,这必然增大程序开销。还有一个重要的问题是:事件冒泡处理可能会激活我们本来不想激活的事件,导致程序错乱,甚至无从下手调试,这常成为对事件冒泡不熟悉程序员的棘手问题。所以必要时,我们要阻止事件冒泡。

阻止事件冒泡

  1. 在W3c中,使用stopPropagation()方法
  2. 在IE下设置cancelBubble = true;

通用版

//阻止事件冒泡函数
function stopBubble(e)
{
    if (e && e.stopPropagation)
        e.stopPropagation()
    else
        window.event.cancelBubble=true
}

JQuery阻止事件冒泡

jQuery对DOM的事件触发具有冒泡特性。有时利用这一特性可以减少重复代码,但有时候我们又不希望事件冒泡。这个时候就要阻止 jQuery.Event冒泡。

在jQuery.Event的文档中的开头得知,jQuery.Event对象是符合W3C标准的一个事件对象,同时jQuery.Event免去了检查兼容IE的步骤。

jQuery.Event提供了一个非常简单的方法来阻止事件冒泡:event.stopPropagation();

$("p").click(function(event){
     event.stopPropagation();
     // do something
})

但是这个方法对使用live绑定的事件没有作用,需要一个更简单的方法阻止事件冒泡:return false;

$("#div1").mousedown(function(event){
    return false;
});

但是这两种方式是有区别的。return false 不仅阻止了事件往上冒泡,而且阻止了事件本身。event.stopPropagation() 则只阻止事件往上冒泡,不阻止事件本身。

preventDefault()

看到网上有人说这个方法也可以,实际上这个是不能阻止冒泡,而是阻止默认行为。比如如果 type 属性是 “submit”,在事件传播的任意阶段可以调用任意的事件句柄,通过调用该方法,可以阻止提交表单。

文章目录
  1. 1. 事件冒泡
    1. 1.1. 注意
  2. 2. 事件捕获
  3. 3. W3C DOM事件流
    1. 3.1. 独特性质
  4. 4. 事件参数
  5. 5. 事件绑定、移除
    1. 5.1. 整合通用版
  6. 6. 事件冒泡优点
  7. 7. 事件冒泡缺点
  8. 8. 阻止事件冒泡
    1. 8.1. 通用版
  9. 9. JQuery阻止事件冒泡
    1. 9.1. preventDefault()