• js的事件机制


    在JavaScript中,有三种常用的绑定事件的方法:

    在DOM元素中直接绑定:onclick="eventFunction"

    在JavaScript代码中绑定:elementObject.onXXX=function(){ // 事件处理代码 }

    绑定事件监听函数:addEventListener() 或 attachEvent() (IE)

    DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

    事件捕获(event capturing):通俗的理解就是,当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内(从window到目标元素)进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件

    事件冒泡dubbed bubbling与事件捕获恰恰相反,事件冒泡顺序是由内到外(从目标元素到window)进行事件传播,直到根节点。

    无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播,它就像一跟引线,只有通过引线才能将绑在引线上的鞭炮(事件监听器)引爆,试想一下,如果引线不导火了,那鞭炮就只有一响了!!!

    dom标准事件流的触发的先后顺序为:先捕获再冒泡,即当触发dom事件时,会先进行事件捕获,捕获到事件源之后进行事件冒泡。不同的浏览器对此有着不同的实现,IE10及以下(不支持addEventListener)不支持捕获型事件,所以就少了一个事件捕获阶段,IE11、Chrome 、Firefox、Safari等浏览器则同时存在。

    事件绑定的方法,如addEventListener:addEventListener(event, listener, useCapture)

    event---(事件名称,如click,不带on);

    listener---事件监听函数;

    useCapture---为true采用事件捕获,若为false采用事件冒泡 ,即为true事件在捕获阶段执行,若为false事件在冒泡阶段执行

    事件的eventPhase属性:它返回一个数字,表示当前的事件处于哪个阶段,可能的值:

    0:NONE

    1:事件流程处于捕获阶段

    2:事件流程处于目标阶段

    3:事件流程处于冒泡阶段

    <body>
        <div id="parent">
            父元素
            <div id="child">子元素</div>
        </div>
    
        <script>
            var parent=document.getElementById("parent");
            var child=document.getElementById("child");
            document.body.addEventListener("click", function(e){
                console.log("click-body", e.eventPhase); 
            }, true);
    
            parent.addEventListener("click", function(e){ 
                console.log("click-parent", e.eventPhase); 
            }, false); 
    
            child.addEventListener("click", function(e){ 
                console.log("click-child", e.eventPhase);
            }, true);
        </script>
    </body>
    
    点击‘父元素’输出:
    click-body 1
    click-parent 3
    点击‘子元素’输出:
    click-body 1  // 由于body绑定的事件在捕获阶段就执行了,所以最先输出
    click-child 2  // 目标阶段
    click-parent 3  // 由于parent绑定的事件在冒泡阶段执行,所以最后输出
    
    
    <body>
        <div id="parent">
            父元素
            <div id="child">子元素</div>
        </div>
    
        <script>
            var parent=document.getElementById("parent");
            var child=document.getElementById("child");
            document.body.addEventListener("click", function(e){
                console.log("click-body", e.eventPhase); 
            }, false);
    
            parent.addEventListener("click", function(e){ 
                console.log("click-parent", e.eventPhase); 
            }, true); 
    
            child.addEventListener("click", function(e){ 
                console.log("click-child", e.eventPhase);
            }, false);
        </script>
    </body>
    
    点击‘父元素’输出:
    click-body 1
    click-parent 3
    点击‘子元素’输出:
    click-parent  1  // 由于parent绑定的事件在捕获阶段就执行了,所以最先输出
    click-child 2  // 目标阶段
    click-body 3  // 由于body绑定的事件在冒泡阶段执行,所以最后输出

    事件触发顺序是由内到外的,这就是事件冒泡,虽然只点击子元素,但是它的父元素也会触发相应的事件,因为子元素在父元素里面,点击子元素也就相当于变相的点击了父元素。如果点击子元素不想触发父元素的事件怎么办?可以停止事件传播 --- event.stopPropagation。

    <body>
        <div id="parent">
            父元素
            <div id="child">子元素</div>
        </div>
    
        <script>
            var parent=document.getElementById("parent");
            var child=document.getElementById("child");
            document.body.addEventListener("click", function(e){
                console.log("click-body", e.eventPhase); 
            }, true);
    
            parent.addEventListener("click", function(e){ 
                console.log("click-parent", e.eventPhase); 
            }, false); 
    
            child.addEventListener("click",function(e){ 
                console.log("click-child", e.eventPhase);
                e.stopPropagation();   // 这里加了阻止事件冒泡的方法
            },false);
        </script>
    </body>
    
    点击‘子元素’输出:
    click-body 1
    click-child 2
    <body>
        <div id="parent">
            父元素
            <div id="child">子元素</div>
        </div>
    
        <script>
            var parent=document.getElementById("parent");
            var child=document.getElementById("child");
            document.body.addEventListener("click", function(e){
                console.log("click-body", e.eventPhase); 
            }, true);
    
            parent.addEventListener("click", function(e){ 
                console.log("click-parent", e.eventPhase); 
            }, true);  // 这里改为在捕获阶段执行,阻止冒泡的方法就不好使了
    
            child.addEventListener("click",function(e){ 
                console.log("click-child", e.eventPhase);
                e.stopPropagation();   // 这里加了阻止事件冒泡的方法
            },false);
        </script>
    </body>
    
    点击‘子元素’输出:
    click-body 1
    click-parent 1
    click-child 2

    大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。

    addEventListener可以添加多个事件处理程序,按照添加它们的顺序进行触发。

    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function(){
        alert(1);
    }, false);
    btn.addEventListener("click", function(){
        alert("Hello world");
    }, false);
    
    // 依次alert数字1和'Hello world'

    通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除,通过addEventListener()添加的匿名函数将无法移除。

    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", funtion(){
        alert(this.id);
    },false);
     
    //这里省略了其他代码
     
    btn.removeEventListener("click", funtion(){    //没有用
        alert(this.id);
    },false)
    var btn = document.getElementById("myBtn");
    var handler = funtion(){
        alert(this.id);
    };
    btn.addEventListener("click", handler, false);
     
    //这里省略了其他代码
     
    btn.removeEventListener("click", handler, false);    //有效

    用元素直接绑定事件

    元素用on绑定的事件是以冒泡的方式向上传播的

    <body>
        <div>
            xxxxx<p>hello, <span>world!</span>
        </div>
    
        <script>
            var div = document.getElementsByTagName("div")[0];
            var p = document.getElementsByTagName("p")[0];
            var span = document.getElementsByTagName("span")[0];
    
            div.onclick = function (event){
                console.log('div');
            }
            p.onclick = function (event){
                console.log('p');
            }
            span.onclick = function (event){
                console.log('span');
            }
        </script>
    </body>
    
    // 点xxxxx输出div
    // 点hello依次输出p div
    // 点world依次输出span p div

    解决元素用on绑定的事件是以冒泡的方式向上传播的问题

    1、用even.target来识别判断

     p.onclick = function (event){
         if(event.target 怎样怎样){
             console.log('p');
         }
     }

    2、调用even.stopPropagation()方法

    p.onclick = function (event){
        console.log('p');
        event.stopPropagation();  
    }

    事件向上传播的用处(其实就是事件委托啦~~~):

    举个例子,一个table有1000个row,每个row都要注册一个onclick函数,为每个row注册事件函数写起来容易,但由于循环多和有太多的函数需要内存管理,效率会下降。这个时候就可以写一个单一的绑定事件函数,绑定到row的父元素,所有row的click event都会向上传播到row的父元素事件函数里来接受处理。1000个事件函数需要注册和管理,现在变成一个,效率自然提升很多。而且新添加的元素还会有之前的事件。当删除某个元素时,不用移解绑上面的click事件。

    事件委托的缺点:

    事件委托基于冒泡,对于不冒泡的事件不支持;

    层级过多,冒泡过程中,可能会被某层阻止掉;

    理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在table上代理td,而不是在document上代理td;

    把所有事件都用代理就可能会出现事件误判。比如,在document中代理了所有button的click事件,另外的人在引用改js时,可能不知道,造成单击button触发了两个click事件。

    on和addEventListener绑定事件的一个区别

    on事件会被后边的on事件覆盖

    // obj是一个dom对象,下同
    // 注册第一个点击事件
    obj.onclick(function(){
        alert("hello world");
    });
    // 注册第二个点击事件
    obj.onclick(function(){
        alert("hello world too");
    });
    
    // 最终只会输出:hello world too

    addEventListener则不会被覆盖

    // 注册第一个点击事件
    obj.addEventListener("click",function(){
        alert("hello world");
    }));
    // 注册第二个点击事件
    obj.addEventListener("click",function(){
        alert("hello world too");
    }));
    
    // 会连续输出:
    // hello world
    // hello world too

    原文:

    https://www.cnblogs.com/jiuyi/p/5460448.html

    https://www.cnblogs.com/zhglhtt/articles/3255670.html

    https://www.cnblogs.com/wangdapeng/p/6601405.html

    https://www.cnblogs.com/Ry-yuan/p/6865632.html

  • 相关阅读:
    centos7下vi的用法
    jQuery的hover方法搭配css的hover选择器,实现选中元素突出显示
    CSS3弹性伸缩布局(下)——flex布局
    什么是架构设计
    界面底部Tab实现
    一行代码实现圆形头像
    免费快速制作个人简历
    基于JS的问卷调查
    基于CSS的个人网页
    纯HTML课表
  • 原文地址:https://www.cnblogs.com/xjy20170907/p/11431323.html
Copyright © 2020-2023  润新知