DOM事件模型与事件委托
DOM事件模型
<div id="grandpa"> <div id="father"> <div id="son">文字 </div> </div> </div> 复制代码
假如给上面3个 div 分别添加事件监听 fnYe/fnBa/fnEr ,那么当点击 文字 的时候会先调用哪个事件?是先调用fnYe?还是先调用fnEr?
此时则需要一种约定来规范事件执行的顺序。
IE5认为应该先调用 fnEr
网景则认为应该先调用 fnYe
2002年,W3C发布了标准。文档名为 DOM Level 2 Events Specification
规定了浏览器应该同时支持两种调用顺序
即:
首先按 爷爷 -> 爸爸 -> 儿子 顺序看有没有函数监听
然后按 儿子 -> 爸爸 -> 爷爷 顺序看有没有函数监听
有监听函数就调用,并提供事件信息,没有就跳过
术语:
从外向内找监听函数,叫事件捕获
从内向外找监听函数,叫事件冒泡
事件绑定API
.addEventListener('click',fn,bool) 复制代码
如果bool不传或者为false
就让fn走冒泡,即当浏览器在冒泡阶段发现有监听函数,就回调用fn,并提供事件信息
如果bool为true
就让fn走捕获,即当浏览器在捕获阶段发现有监听函数,就回调用fn,并提供事件信息
--HTML-- <div id="grandpa">grandpa <div id="father">father <div id="son">son</div> </div> </div> --Javascript-- let fnYe = document.getElementById('grandpa') let fnBa = document.getElementById('father') let fnEr = document.getElementById('son') fnYe.addEventListener('click', function fn() { console.log('爷爷') } ) fnBa.addEventListener('click', function fn() { console.log('爸爸') } ) fnEr.addEventListener('click', function fn() { console.log('儿子') }) 复制代码
上面代码没有bool值,打印顺序为 儿子 爸爸 爷爷
--HTML-- <div id="grandpa">grandpa <div id="father">father <div id="son">son</div> </div> </div> --Javascript-- let fnYe = document.getElementById('grandpa') let fnBa = document.getElementById('father') let fnEr = document.getElementById('son') fnYe.addEventListener('click', function fn() { console.log('爷爷') },true) fnBa.addEventListener('click', function fn() { console.log('爸爸') },true) fnEr.addEventListener('click', function fn() { console.log('儿子') },true) 复制代码
上面代码bool值为true,打印顺序为 爷爷 爸爸 儿子
事件委托
由于事件会在冒泡阶段向上传播到父级元素,因此可以把子元素的监听函数绑定在父级元素上,由父级元素的监听函数统一处理多个子元素的事件。这种方法叫做事件委托。
例如,我们想要租房子。如果是我们自己租就比较麻烦,需要跑小区。此时我们可以委托中介帮我们操作,所谓的委托就是委托中介帮我们操作本应该由我们自己操作的事情。
情景一
给100个按钮添加点击事件
倘若给每一个按钮都绑定点击事件,那将会占用大量内存。我们可以监听这100个按钮的祖先,等冒泡的时候判断target是不是这100个中的一个。
--html-- <div id="div1"> <span>span</span> <button data-id = "1号">click1</button> <button>click2</button> <button>click3</button> <button>click4</button> <button data-id = "5号">click5</button> <button>click6</button> ... <button>click100</button> </div> --javascript-- div1.addEventListener('click', (e) => { const t = e.target if (t.tagName.toLowerCase() === 'button') { console.log('botton被点击') console.log('botton' + t.textContent + '被点击') console.log('botton' + t.dataset.id + '被点击') } }) 复制代码
情景二
监听目前不存在的元素的点击事件
由于目前元素不存在,无法直接监听这个元素。但我们可以使用事件委托实现,监听祖先元素,等点击的时候看是不是想要的元素。
--html-- <div id="div1"> </div> --javascript-- setTimeout(()=>{ const button = document.createElement('button') button.textContent = 'click' div1.appendChild(button) },1000) div1.addEventListener('click', (e) => { const t = e.target if (t.tagName.toLowerCase() === 'button') { console.log('botton被点击') } }) 复制代码
事件委托的优点
省内存,提高性能。
可以监听动态元素。
作者:jwoonghuang
链接:https://juejin.cn/post/7026738178568486948