事宜托付和冒泡机制有关联吗?

口试官提出的题目

我们在口试前端的过程当中,常常会听到口试官问如许的题目:

假如我有一个页面,内里1000个元素都要绑定click事宜,叨教你要如何做

假如你回复逐一绑定那预计能够直接回家了,口试官愿望的答案是你来放言高论事宜托付,你应当能给出要领并写出解决方案。
接下来,考官肯定要问,这么做的优点是什么,或许你为何用事宜托付。

我以为优点主要有两个:

  • 事宜只须要绑定一次,而不是绑定1000次,进步效力
  • 动态添加进父级的元素自动具有事宜

依据http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/ 更专业的说法以下:

  • Fewer functions to manage. 治理较少的函数
  • Takes up less memory. 更少的内存斲丧
  • Fewer ties between your code and the DOM.下降代码和dom之间的关联
  • Don’t need to worry about removing event handlers when changing the DOM via修正dom的时刻不必斟酌删除事宜。

这还没完,口试官一般会接下来问,那末,你绑定在父级上,页面如何晓得你点击的是哪一个?

我想这应当回复,应用了事宜的冒泡机制。

等等,挺在这里,虽然不仅一篇文章论述了事宜托付是应用了冒泡机制,得益于冒泡机制,然则,如何得益的,如何应用的。

因而猎奇的我引伸出别的一个庞杂的议题,事宜的绑定和实行机制。

事宜实行

许多文章里引见了这个机制,这篇文章很简明详实:
http://www.quirksmode.org/js/events_order.html

归纳综合一下,就是由于汗青缘由,浏览器对事宜的处置惩罚有两种形式,一种是先实行表面父级元素的事宜(捕捉形式 capturing),一种先实行内部元素的事宜(冒泡形式bubbling)。这个看法就相似图层,一种是表面的在前面,另一种是内里在前面。

当代浏览器里参照w3c范例,采纳了这两种体式格局并行的体式格局,简朴的来讲就是先捕捉,再冒泡。(为何,为何,规定为一种不好么)

我们都晓得为某个元素注册事宜,是经由过程addEventListener这个要领,那末,我直接注册的事宜,他是属于捕捉实行呢,照样冒泡实行呢?照样说我捕捉阶段实行一次,冒泡阶段再实行一次。难道说每次都实行两次?

不对,一样一次绑定的要领是实行一次,由于它要么属于捕捉阶段,要么属于冒泡阶段。这两个阶段就像通向公司的两条路,你去公司是一条路,返来是别的一条路,你的事宜是路上的小卖铺,它要么在去的那条路上,要么在返来的那条路上。固然你也能够两条路上都开一家小卖铺,虽然说没什么意义,然则如许事宜触发的时刻确切实行了两次,不过这也能证实事宜实行的两个阶段。

事宜实行递次的实验

先忘记上面的理论,下面我们来做个实验,记着下面的html,我们盘算为out同时绑定两个事宜,看看实行递次是如何的

<div id="out"> 
    <div id='inner1'>
       Click
    </div>
</div>
var a = document.querySelector('div#out')
var b = document.querySelector('div#inner1')

a.addEventListener('click',function(e){
   alert(e.target+'事宜A')
})
a.addEventListener('click',function(e){
   alert(e.target+'事宜B') 
})

我们能够看到,为同一个元素前后绑定两个事宜,实行的递次是从上到下的,把事宜B提到A前面就会先实行B。

接下来我们试一下经由过程addEventListener第三个参数指定事宜绑定在哪条路上,false为冒泡阶段,true为捕捉阶段。

a.addEventListener('click',function(e){
    alert(e.target+'外部元素在冒泡阶段')
},false)

a.addEventListener('click',function(e){
    alert(e.target+'外部元素在捕捉阶段')
},true)

这下我们能够看到,不管冒泡在前面照样背面,都是先实行捕捉阶段的那行代码,左证了先前说道的w3c范例下的先实行捕捉再实行冒泡的行动。

不过在这里的实验中,我无意发现了一个风趣的征象,当你把html改成没有子元素,比方

<div id="out"> 
       Click
</div>

这时刻就不遵照先捕捉再冒泡的准绳了,看起来像是推断节点没有子元素,就不须要运用捕捉和冒泡的流程,只采纳先来后到的递次。这个中的道理还望高手指教。

总结: 当一个页面元素包括子元素节点的时刻,他在处置惩罚在其身上的绑定事宜的时刻,采纳先实行捕捉阶段的事宜,再实行冒泡阶段的事宜。而事宜处于哪一个阶段,是由addevnetlistener的第三个参数决议的。

阻挠冒泡

我们都晓得,阻挠冒泡是采纳相似 stopPropagation()的要领。然则请斟酌如许一个题目:

a.addEventListener('click',function(e){
    alert(e.target+'外部元素在冒泡阶段')
},false)
a.addEventListener('click',function(e){
    alert(e.target+'外部元素在捕捉阶段')
},true)

b.addEventListener('click',function(e){
    e.stopPropagation()
    alert(e.target+'内部元素上的事宜')
})

这段代码里,我点击b,事宜触发的递次是如何的?
答案是:外部元素捕捉 —> 内部元素事宜

由于捕捉是永久优先实行的,内部元素由于不存在子元素,所以只要一个阶段,无所谓先实行后实行,由于自身没有冒泡事宜,所以stoppropagation() 擦过自身,寻觅父级的冒泡阶段上的事宜,一次查找,悉数给阻挠掉。

所以,想要点击内部的时刻疏忽外部事宜,肯定不要把外部的事宜放在捕捉阶段,就是说第三个参数不要设为true。

我们再把代码搞更庞杂一些:

<div id="out">
    <div id="inner1">
      <div id="inner2">
        click 
      </div>
    </div>
</div>
var a = document.querySelector('div#out')
var b = document.querySelector('div#inner1')
var c = document.querySelector('div#inner2')

a.addEventListener('click',function(e){
    alert('a在冒泡阶段') 
},false)
a.addEventListener('click',function(e){
    alert('a在捕捉阶段')
},true)

b.addEventListener('click',function(e){
    alert('b在冒泡阶段')
},false)
b.addEventListener('click',function(e){
    alert('b在捕捉阶段')
},true)

c.addEventListener('click',function(e){
    alert(e.target+'内部元素事宜')
})

实行一遍,能够加深了对事宜这个形式的明白,递次是如许的

a捕捉 —> b捕捉 —> 内部事宜 —> b冒泡 —> a冒泡

这里我感兴趣的是阻挠冒泡会如何,测下来是,假如把stoppropagation() 放在b,b自身的冒泡照样会实行,那末同理假如放在c,c自身假如有冒泡事宜也会实行,

所以stoppropagation()所做的事变能够这么明白,阻挠父级元素冒泡阶段的事宜。

事宜托付和冒泡机制有关联吗?

接下来我想引出本文的重点:事宜托付和冒泡机制有关联吗?
我以为就算有关联,关联也不大。

我们先来看一下一个罕见的事宜托付例子:

// Get the element, add a click listener...
document.getElementById("parent-list").addEventListener("click",function(e) {
    // e.target is the clicked element!
    // If it was a list item
    if(e.target && e.target.nodeName == "LI") {
        // List item found!  Output the ID!
        console.log("List item ",e.target.id.replace("post-")," was clicked!");
    }
});

简言之,绑定在父类上一个事宜,然后经由过程回调函数的参数取得当前点击的是哪一个元素,相当于把事宜绑定在子元素身上。

叨教这跟上文言简意赅的冒泡机制有什么联络?

假定不存在冒泡或许捕捉,在父类上点击到了子类或许不管点到哪,这个事宜都是要实行的,子类这个元素还会作为援用传到函数体里,我着实看不出这个冒泡有什么关联,假如要什么致使事宜托付能够完成,应当是函数体内的援用才是

那末,这个援用是什么状况呢,继承上面的实验:

var a = document.querySelector('div#out')
var b = document.querySelector('div#inner1')
var c = document.querySelector('div#inner2')

a.addEventListener('click',function(e){
    alert('a在冒泡阶段')
    console.log(e.target)//inner2
    console.log(this)//out
    console.log(e.currentTarget)//out
},false)
a.addEventListener('click',function(e){
    alert('a在捕捉阶段')
    console.log(e.target)//inner2
},true)

b.addEventListener('click',function(e){
    alert('b在冒泡阶段')
    console.log(e.target)//inner2
},false)
b.addEventListener('click',function(e){
    alert('b在捕捉阶段')
    console.log(e.target)//inner2
},true)

c.addEventListener('click',function(e){
    alert(e.target+'内部元素事宜')
    console.log(e.target)//inner2
})

我们能看到,不管在哪一层里,e.target都是你当前点击的自身,这绝不新鲜,由于e自身是一个event对象,比方这里的MouseEvent,内里还带了是不是同时按下alt键,鼠标位置等信息,可见这个对象自身能够说是和绑定主体无关了,和事宜有关。所以,和冒泡照样没啥关联。以上代码里还展现了两种猎取事宜实行主体的要领,分别是e.currentTarge 和 this

所以我的看法是,虽然提到js的事宜托付一般都邑联络到冒泡,然则就算当初没有设想冒泡和捕捉,事宜托付照样事宜托付,它依靠的是event对象通报到监听函数内里了,和其他无关。

    原文作者:fishenal
    原文地址: https://segmentfault.com/a/1190000000470398
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞