js 捕获型事件、冒泡型事件、DOM事件流、事件委托

来源:互联网 发布:雪河二少捏脸数据 编辑:程序博客网 时间:2024/06/05 10:03

引言:
首先考虑这么一个问题,当在页面画很多同心圆,当你手机放在同心圆圆心的时候,你觉得你的手是放在所有圆内还是只是在你最近的那个圆。
那么还有一个问题,如果你觉得上面那个问题的答案是手指放在全部的圈内,那么你觉得应该怎么给圈排序的,是从最外面的圈开始还是从最近的圈开始呢?

一、事件流
其实说的问题就是接下来要说的事件流的问题了。当子节点和父节点都有点击事件,然后点击的是子节点,那么这时候先触发子节点还是父节点的事件呢?IE是这么想的,先触发子节点后触发父节点(冒泡型事件)。Netscape团队提出的来的却是先触发父节点(捕获型事件)。这就是事件流。

二、事件冒泡
IE事件流称为事件冒泡,即事件开始时由最具体的元素(文档中嵌层最深的那个节点)接受,然后逐次往上传播到较为不具体的节点(文档)。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xHTML1/DTD/xHTML1-transitional.dtd"><HTML XMLns="http://www.w3.org/1999/xHTML" lang="gb2312"><head><title></title><meta name="keywords" content="JS,事件冒泡,cancelBubble,stopPropagation" /><div id = "mydiv">Click</div></body></HTML> 

比如我点击Click,那么事件会按照如下顺序传播:
div、body、html、document

三、捕获型事件
跟事件冒泡刚好相反,上述的例子顺序为:
document、html、body、div
事件捕获是很少人用的,冒泡用的比较多

四、DOM事件流(DOM2级事件)
DOM事件流有三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。其中事件捕获阶段不会接受事件

五、事件委托
既然说到了事件冒泡就说一个利用事件冒泡来做事件委托。
那么什么叫做事件委托呢?
对“事件处理程序过多”问题的解决方案就是事件委托,事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件,例如click事件会一直冒泡到document层次,也就是说我们可以为整一个页面指定一个onclick事件处理程序,而不必为每一个可以单击的元素添加事件处理程序。
那么这么做有什么必要吗?
首先,添加到页面的事件处理程序数量直接关系到页面整体运行性能。原因一:每个函数都是一个对象,每一个对象都会占用内存,内存对象越多性能自然就差了。原因二:每次指定事件都需要访问dom节点,dom节点访问次数越多,会延迟整个页面的交互就绪事件。
看下面的一段代码:

<ul id="links">    <li id = "l1">1</li>    <li id = "l2">2</li>    <li id = "l3">3</li></ul><script>    var EventUtil = {        addHandler: function(element, type, handler){    //若浏览器支持addEventListener,则使用addEventListener来添加事件            if(element.addEventListener){      //DOM2级                element.addEventListener(type, handler, false);            } else if(element.attachEvent){    //IE                element.attachEvent("on" + type, handler);            } else {            //若以上两种添加事件的方法都不支持,则使用默认的行为来添加事件                element["on" + type] = handler; //DOM0            }        },        //移除事件        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;        },        getTarget :function (event) {            return event.target || event.srcElement;        },        stopPropagation: function (event) {            if (event.stopPropagation){                event.stopPropagation();            } else {                event.cancelBubble = true;            }        },        preventDefault: function (event) {            if (event.preventDefault){                event.preventDefault();            } else {                event.returnValue = false;            }        }     }      var links = document.getElementById('links');    EventUtil.addHandler(links, "click" ,function(event) {        event = EventUtil.getEvent(event);        var target  = EventUtil.getTarget(event);        switch(target.id) {            case "l1" :                document.title = "Change the document title";                break;            case "l2" :                location.href = "http:www.baidu.com";                break;            case "l3" :                alert ('l3');                break;        }    });

这段代码实现了三个不同li节点的点击事件,但是只有一个事件处理程序就可以了,节省函数的同时也节省了访问节点的次数。(注意应该选取target节点作为对象)

Tips:
这里用到了一个EventUtil 对象来处理跨浏览器事件处理程序。
主要有三种不同的事件处理程序:
1、DOM0级事件处理程序
这是一种传统的事件处理程序,就是将一个函数赋值给一个事件处理程序属性。
例如:

//添加var btn = document.getElementById("mybtn");btn.onclick = function (){    alert(this.id);}//删除btn.click = null;   

2、DOM2级事件处理程序
使用addEventListener,removeEventListener指定或删除事件处理程序

//addEventListener(type,handler,false)//removeEventListener(type,handler,false)var btn = document.getElementById("mybtn");var handler = function (){    alert(this.id);}btn.addEventListener = ('click',handler,false);//删除btn.removeEventListener= ('click',handler,false);

3、IE事件处理程序
使用attachEvent,detachEvent指定或删除事件处理程序

//attachEvent(type,handler)//detachEvent(type,handler)var btn = document.getElementById("mybtn");var handler = function (){    alert(this.id);}btn.attachEvent= ('onclick',handler);//删除btn.detachEvent= ('onclick',handler);

事件对象event作为window对象的一个属性存在(DOM0和DOM2直接用event表示)
目标对象作为event中srcElement属性存在(DOM0和DOM2用event里面的target属性存在)

六、消除冒泡
可以用stopPropagation来消除冒泡行为,看一下经典例子

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xHTML1/DTD/xHTML1-transitional.dtd"><HTML XMLns="http://www.w3.org/1999/xHTML" lang="gb2312"><head><title> 阻止JS事件冒泡传递(cancelBubble 、stopPropagation)</title><meta name="keywords" content="JS,事件冒泡,cancelBubble,stopPropagation" /><script>    var EventUtil = {        addHandler: function(element, type, handler){    //若浏览器支持addEventListener,则使用addEventListener来添加事件            if(element.addEventListener){      //DOM2级                element.addEventListener(type, handler, false);            } else if(element.attachEvent){    //IE                element.attachEvent("on" + type, handler);            } else {            //若以上两种添加事件的方法都不支持,则使用默认的行为来添加事件                element["on" + type] = handler; //DOM0            }        },        //移除事件        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;        },        getTarget :function (event) {            return event.target || event.srcElement;        },        stopPropagation: function (event) {            if (event.stopPropagation){                event.stopPropagation();            } else {                event.cancelBubble = true;            }        },        preventDefault: function (event) {            if (event.preventDefault){                event.preventDefault();            } else {                event.returnValue = false;            }        }     } function doSomething (obj,evt) { alert(obj.id); var e=(evt)?evt:window.event; if (window.event) {  e.cancelBubble=true;// ie下阻止冒泡 } else {  //e.preventDefault();  EventUtil.stopPropagation(e);// 其它浏览器下阻止冒泡 }}</script></head><body><div id="parent1" onclick="alert(this.id)" style="width:250px;"> <p>This is parent1 div.</p> <div id="child1" onclick="alert(this.id)" style="width:200px;">  <p>This is child1.</p> </div> <p>This is parent1 div.</p></div><br /><div id="parent2" onclick="alert(this.id)" style="width:250px;"> <p>This is parent2 div.</p> <div id="child2" onclick="doSomething(this,event);" style="width:200px;">  <p>This is child2. Will bubble.</p> </div> <p>This is parent2 div.</p></div></body></HTML> 

结果:
当点击parent1的时候只触发parent1,当点击child1的时候parent1和child1都会触发
当点击parent2的时候只触发parent2,当点击child2的时候只触发child2
说明child2的冒泡被取消了

阅读全文
0 0