js的事件机制

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

在DOM元素中直接绑定:οnclick="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

转载于:https://www.cnblogs.com/xjy20170907/p/11431323.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值