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

4 篇文章 0 订阅

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

一、事件流
其实说的问题就是接下来要说的事件流的问题了。当子节点和父节点都有点击事件,然后点击的是子节点,那么这时候先触发子节点还是父节点的事件呢?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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值