事件委托

775 阅读3分钟

事件的触发

在浏览器大战的时代,微软和网景因为其中的DOM事件的触发顺序引发了一场争论。
以点击事件为例,我们来看看这场争论的内容。

html:
<div class="grandpa">
	<div class="father">
           <div class="son">
        	点我
           </div>
    </div>
</div>
js:
let son = document.querySelector(".son")
.son.addEventlistener("click",()=>{console.log(1)})

对于这段代码,当son被点击时,通知的顺序应该是什么? 也就是说,我应该从grandpa到father到son的顺序依次扫描是否被点击,还是从son到father到grandpa依次通知被点击这个事件。出现了两种不同的分歧。 这是相关示意图,从外到里的这种叫做捕获阶段,从里往外的叫做冒泡阶段

最后这事情W3C将这两种顺序合成了一种方法,定义为adEventListener,标准规定,一个浏览器应该支持两种的思路,即首先事件应该先进行一次捕获阶段,然后在经历一次冒泡阶段,在addEventListener中规定三个参数,第一个是触发的事件名,第二个是绑定的函数,第三个是决定是否开启捕捉。 依靠理论,我们用下列代码实践看看:

html:
<div class="div1">
    <div class="div2">
        <button class="div3">点我</button>
    </div>
</div>
js:
const grandpa = document.querySelector(".div1");
    const  father = document.querySelector(".div2")
    const  son = document.querySelector(".div3")
grandpa.addEventListener("click",(e)=>{
        console.log(e.currentTarget);
    },true)

    father.addEventListener("click",(e)=>{
        console.log(e.currentTarget);
    },true)
    son.addEventListener("click",(e)=>{
        console.log(e.currentTarget);
    },true)    


此时开启了事件捕捉,所以在捕捉阶段,首先执行了grandpa,再是father,最后才是son,但是如果去掉true,得到的结果是
其中,当事件执行的时候,浏览器会给js一个对象e,他里面包含了currentTarget和target,currentTarget是指的当前被监听的元素,而target是用户操作的元素。简单的来说,currentTarget可以是target本身也可以使target的父类。 比如:div>span{你好},此时点击你好,e.target就是span,e.currentTarget就是div
当只有一个div被监听的时候,捕获阶段和冒泡阶段执行顺序会按照定义的先后执行,并不严守先捕获再冒泡的情况。
捕获不能被打断,但是冒泡可以取消,当在e上面写上e.stopProgation(),此时的冒泡就能被中断,浏览器不再会往上走了。
查阅mdn,可以看到一些词语是有关冒泡的,Bubbles表示是否冒泡,Cancelable表示是否支持开发者取消冒泡,有些特例比如scroll不支持取消冒泡,但是它可以用wheel和touchstart来取消相关默认滚动事件。

事件委托

依靠着事件冒泡和e这个对象,我们将有些绑定相同函数的同级元素用一个父元素包裹起来,然后在父元素上绑定需要绑定事件的函数。这是时候因为只需要绑定一个父类,代码量大大的减少,这是用事件委托的代码,只需绑定一个父类,却实现了效果

html:
<div class="fa">
    <button class="button1">click me</button><button class="button2">click me</button><button class="button3">click me</button><button class="button4">click me</button><button class="button5">click me</button><button class="button6">click me</button>
</div>
js:
const fa = document.querySelector(".fa");
    fa.addEventListener("click",(e)=>{
        console.log(e.target.classList.value)
    })

此时,因为有e.target存在,我们减少了五个方法的绑定

自定义事件

我们可以自定义一些事件来定义它能否冒泡

xxx.addEventListener('click', ()=>{
  const event = new CustomEvent("xiaoznzk", {"detail":{name:'xiaoznz', age: 18}})
  xxx.dispatchEvent(event)
})

xxx1.addEventListener('xiaoznz', (e)=>{
  console.log('xiaoznz')
  console.log(e)
})

此时就会执行一个叫xiaoznz的事件。