DOM 事件模型
DOM的事件模型分为两种:
- 捕获: 从外向内找监听函数,叫做事件捕获
- 冒泡:从内到外找监听函数,叫做事件冒泡
DOM的事件机制
- 捕获阶段:事件从window对象自上而下向目标节点传播
- 目标阶段:目标节点处理事件
- 冒泡阶段:事件从目标节点自下而上向window对象传播
事件绑定API
IE5: no2.attachEvent('onclick',fn) //默认冒泡
网景: no2.addEventListener('click',fn) //默认捕获
W3C: no2.addEventListener('click',fn,bool) // bool不传或者为falsy,则走冒泡。为true,则走捕获
取消冒泡
捕获不可取消,但冒泡可以取消
e.stoPropagation() //取消冒泡
在 MDN 上面可以搜索scroll event,可以看到bubbles和Cancelable。
- Bubbles 的意思是该事件是否支持冒泡,所有冒泡都可以取消。
- Cancelable 的意思是开发者是否可以阻止默认事件,与冒泡无关。
事件委托
事件委托通俗来说就是监听祖先元素。
由于冒泡阶段会从子节点向上传播到父节点,因此可以监听父节点来处理子节点的事件。
优点:
- 由于只需要监听一个父节点不需要一个个监听子节点,可以节约监听数,减少内存消耗。
- 可以监听动态生成的元素
//简单封装事件委托
function on(eventType, element, selector, fn){
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType, (e) => {
const t = e.target
// 如果元素被指定的选择器字符串选择,Element.matches() 方法返回true; 否则返回false。
if(t.matches(selector)){
fn(e)
}
})
}
on('click', '#div1', 'button', () => {
console.log('button 被点击了')
})
以下是扩展内容:上述代码存在bug
// js
setTimeout(() => {
const button = document.createElement('button')
const span = document.createElement('span')
span.textContent = 'click'
button.aooendChild(span)
div1.appendChild(button)
})
// 此时被操作的元素为span,即 e.target为span
// 上述代码bug:此时,若使用上述代码封装事件委托,点击按钮将无反应
<!-- html结构 -->
<div id="div1">
<button>
<span>click</span>
</button>
</div>
// 封装事件委托
function on(eventType, element, selector, fn) {
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType, e => {
let el = e.target
while (!el.matches(selector)) {
if (element === el) {
// 往上找祖先元素,不可超过element
el = null
break
}
el = el.parentNode
}
el && fn.call(el, e, el)
})
return element
},