事件机制(事件冒泡与事件捕获)

<div id="outer">
    <p id="inner">Click me!</p>
</div>

事件冒泡

微软提出了名为事件冒泡(event bubbling)的事件流。时间冒泡就是指事件会从最内层的元素开始发生,一直向上传播,直到document对象。

因此上面的例子在事件冒泡的概念下发生click事件的顺序应该是p -> div -> body -> html -> document

事件捕获

网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

上面的例子在事件捕获的概念下发生click事件的顺序应该是document -> html -> body -> div -> p

Netscape Navigator不会将页面上的很多元素暴漏给事件

在DOM事件流中,“DOM2级事件”中规定的事件流同时支持了事件捕获阶段事件冒泡阶段

穿插一个小片段,解释一下这里的dom2级事件。

使用事件处理程序有三种方式:

1、HTML事件处理程序

 

如:<div id="outer">
    <p id="inner" onclick="alert("我是html事件处理程序");">Click me!</p>
</div>


2、DOM0级事件处理程序
如:<div id="outer">
    <p id="inner">Click me!</p>
</div>
<script type="text/javascript">
var pBtn=document.get
ElementById("inner");

pBtn.onclick=function(){
}

</script>

 

3、DOM2级事件

  就是用addEventListener()等类型的,在事件处理上IE和DOM也是有区别的,

  IE中绑定事件和解绑为attachEvent(),detachEvent(),

  DOM中的为addEventListener(),removeEventListener()

  那么如何兼容此问题呢?如下代码,通过if..else类型判断

 <body>
      <div id="box">
         <input type="button" value="按钮" id="btn" onclick="showMes()">
         <input type="button" value="按钮2" id="btn2">
         <input type="button" value="按钮3" id="btn3">
         <a href="event.html" id="go">跳转</a>
      </div>
   </body>
html

 

var eventUtil={
             // 添加句柄
             addHandler: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;
               }
             },
             // 删除句柄
             removeHandler: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;
               }
             },
          getEvent:function(event){
            return event?event:window.event;
          },
          getType:function(event){
            return event.type;
          },
          getElement:function(event){
            return event.target || event.srcElement;
          },
          preventDefault:function(event){
            if(event.preventDefault){
              event.preventDefault();
            }else{
              event.returnValue=false;
            }
          },
         stopPropagation:function(event){
           if(event.stopPropagation){
             event.stopPropagation();
           }else{
             event.cancelBubble=true;
           }
         }
  }
js
window.onload=function(){
  var go=document.getElementById('go'),
      box=document.getElementById('box');

  eventUtil.addHandler(box,'click',function(){
      alert('我是整个父盒子');
  });

  eventUtil.addHandler(go,'click',function(e){
      //e=eventUtil.getEvent(e);
      e=e || window.event;
      alert(eventUtil.getElement(e).nodeName);
      eventUtil.preventDefault(e);
      eventUtil.stopPropagation(e);
  });

}
js

 

应用例子:

<ul id="color-list">
    <li>red</li>
    <li>yellow</li>
    <li>blue</li>
    <li>green</li>
    <li>black</li>
    <li>white</li>
</ul>

如果点击页面中的li元素,然后输出li当中的颜色,我们通常会这样写:

(function(){
    var color_list = document.getElementById('color-list');
    var colors = color_list.getElementsByTagName('li');
    for(var i=0;i<colors.length;i++){                          colors[i].addEventListener('click',showColor,false);
    };
    function showColor(e){
        var x = e.target;
        alert("The color is " + x.innerHTML);
    };
})();

这样的代码再正常不过了,但是,如果页面上有几百个元素需要绑定(假设),那么务必就要绑定几百次啦。
这样问题就出现了:

第一:大量的事件绑定,性能消耗,而且还需要解绑(IE会泄漏)
第二:绑定的元素必须要存在
第三: 后期生成HTML会没有事件绑定,需要重新绑定
第四: 语法过于繁杂

 

在实际的开发当中,利用事件流的特性,我们可以使用一种叫做事件代理的方法。

利用事件流的特性,我们只绑定一个事件处理函数也可以完成:

(function(){
    var color_list = document.getElementById('color-list');
    color_list.addEventListener('click',showColor,false);
    function showColor(e){
        var x = e.target;
        if(x.nodeName.toLowerCase() === 'li'){
            alert('The color is ' + x.innerHTML);
        }
    }
})();

使用事件代理的好处不仅在于将多个事件处理函数减为一个,而且对于不同的元素可以有不同的处理方法。假如上述列表元素当中添加了其他的元素(如:a、span等),我们不必再一次循环给每一个元素绑定事件,直接修改事件代理的事件处理函数即可。

关于事件对象

在IE中事件对象是windows的一个属性event

oDiv.onclick=function(){

  var oEvent=window.event;

}

DOM标准则说,event对象必须作为唯一参数传给事件处理函数,

oDiv.onclick=function(){

  var oEvent=window.arguments【0】;

}

或者

oDiv.onclick=function(oEvent){

}

可用event.type获取事件对象的类型

event.target获取事件对象的目标

event.stoppropagation()阻止冒泡

 

在Jquery中如何用呢?

 

具体来说,事件委托就是事件目标自身不处理事件,而是把处理任务委托给其父元素或者祖先元素,甚至根元素(document)

委托这么好的特性 jQuery 当然不会放过,所以就衍生出  .bind()、.live() .on()和.delegate(),jQuery 的事件绑定有多个方法可以调用,以 click 事件来举例:

click方法
bind方法
delegate方法
on方法

这里要清楚的认识:不管你用的是(click / bind / delegate)之中哪个方法,最终都是 jQuery 底层都是调用 on 方法来完成最终的事件绑定。因此从某种角度来讲除了在书写的方便程度及习惯上挑选,不如直接都采用 on 方法来的痛快和直接。

所以在新版的 API 中都这么写到:

.on()方法事件处理程序到当前选定的 jQuery 对象中的元素。在jQuery 1.7中,.on()方法提供绑定事件处理的所有功能、效果不言而喻了,除了性能的差异,通过委托的事件还能很友好的支持动态绑定,只要 on 的delegate 象是 HTML 页面原有的元素,由于是事件的触发是通过Javascript的事件冒泡机制来监测,所以对于所有子元素(包括后期通过JS生成的元素)所有的事件监测均能有效,且由于不用对多个元素进行事件绑定,能够有效的节省内存的损耗。

 

posted @ 2015-06-20 21:22  夏目の-の  阅读(561)  评论(0编辑  收藏  举报