一本正经学前端

js中的事件传播过程

2017-10-10  本文已影响63人  胡思乱想的Alice

引子:
父div添加监听事件,子div设置监听事件,点击子元素,会出现什么结果

事件传播的三个过程,事件捕获阶段、处于目标阶段、事件冒泡阶段。
事件捕获由远及近逐渐靠近事件目标,事件冒泡由事件目标逐渐向上冒泡
那是不是所有的事件都要经历这三个过程呢?其实不然

IE:它认为事件流应该是事件冒泡。
Netscape:它则认为事件流应该是事件捕获。
W3C:首先是事件捕获然后事件冒泡

在支持w3c的浏览器中,程序员可以通过设置addEventListener(type,handler,useCapture)中的userCapture值来决定元素是在冒泡阶段执行事件还是捕获阶段执行,默认为false,即冒泡阶段。
IE没有提供选择,事件只能在冒泡阶段捕获。
偷个图[https://www.w3cin.com/2016/06/03/%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84DOM%E4%BA%8B%E4%BB%B6/]

事件传播三个阶段
IE9、Opera、Firefox、Chrome和Safari都支持DOM事件流。
写个代码测试一下
页面内容很简单,父div子div
<div id="parent">
        parent
        <div id="child">child</div>
    </div>

因为考虑到浏览器兼容性,先写一个跨浏览器事件处理函数

var eventHandler={
            addEventHandler:function(ele,type,handler,capture){
                if(ele.addEventListener){
                    ele.addEventListener(type,handler,capture);
                }else if(ele.attachEvent){
                    //ie下是需要加on的
                    //ele.attachEvent(type,handler);
                    ele.attachEvent('on'+type,handler);
                }else{
                    ele['on'+type]=handler;
                }
            },
            stopPropagation:function(event){
                event=event||window.event;
                if(event.stopPropagation){
                    event.stopPropagation();
                }else{
                    event.cancelBubble=true;
                }
            },
            preventDefault:function(event){
                event=event||window.event;
                if(event.preventDefault){
                    event.preventDefault();
                }else{
                    event.returnValue=true;
                }
            }
        }

进入正题,对父子div添加监听函数

window.onload=function(){
            var parent=document.getElementById("parent");
            var child=document.getElementById("child");
            eventHandler.addEventHandler(parent,'click',function(e){
                console.log("parent捕获阶段");
            },true);
            eventHandler.addEventHandler(parent,'click',function(e){
                console.log("parent冒泡阶段");
            },false);
            eventHandler.addEventHandler(child,'click',function(e){
                console.log("child冒泡阶段");
            },false);
            eventHandler.addEventHandler(child,'click',function(e){
                console.log("child捕获阶段");
            },true);
        }

chrome下测试结果:

image.png

事件果然是从父div到子div被捕获,然后从子div到父div冒泡。
注意:此处对子div绑定了两个事件,即冒泡事件和捕获事件,这两个事件的执行顺序是按照绑定的先后顺序执行的,如果把两个执行函数替换一下,结果就会先打印child捕获阶段,再打印child冒泡阶段,不信你试试~

ie8下的测试结果:

image.png

ie下不存在捕获阶段,直接从冒泡阶段开始,由子div到父div打印事件。此处应该注意,对重复绑定的事件,在IE9以下的浏览器中执行顺序都是反着的,因为IE9开始已经支持addEventListener()方法所以不会在有这个问题了。
比如现在我把事件的执行顺序改为

eventHandler.addEventHandler(parent,'click',function(e){
                console.log("parent捕获阶段");
                // eventHandler.stopPropagation(e);
            },true);
            eventHandler.addEventHandler(parent,'click',function(e){
                console.log("parent冒泡阶段");
            },false);
            eventHandler.addEventHandler(child,'click',function(e){
                console.log("child捕获阶段");
            },true);
            eventHandler.addEventHandler(child,'click',function(e){
                console.log("child冒泡阶段");
            },false);

chrome下的执行结果为:

image.png

ie8下的执行结果为:

image.png
上一篇下一篇

猜你喜欢

热点阅读