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被点击')
}
})
复制代码
事件委托的优点
- 省内存,提高性能。
- 可以监听动态元素。