DOM事件模型与事件委托
DOM事件模型
捕获与冒泡
事件冒泡和捕捉是两种机制,主要描述当在一个元素上有两个相同类型的事件处理器被激活会发生什么。
当一个事件发生在具有父元素的元素上:
捕获
在捕获阶段:
- 浏览器检查元素的最外层祖先
<html>
,是否在捕获阶段中注册了一个onclick
事件处理程序,如果是,则运行它。 - 然后,它移动到
<html>
中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。
冒泡
在冒泡阶段,恰恰相反:
- 浏览器检查实际点击的元素是否在冒泡阶段中注册了一个
onclick
事件处理程序,如果是,则运行它 - 然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达
<html>
元素。
事件捕获vs事件冒泡
当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢。
这里记被点击的DOM节点为target节点
- document 往 target节点,捕获前进,遇到注册的捕获事件立即触发执行
- 到达target节点,触发事件(对于target节点上,是先捕获还是先冒泡则捕获事件和冒泡事件的注册顺序,先注册先执行)
- target节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发
总结就是:
- 对于非target节点则先执行捕获在执行冒泡
- 对于target节点则是先执行先注册的事件,无论冒泡还是捕获
- 对于事件代理来说,在事件捕获或者事件冒泡阶段处理并没有明显的优劣之分,但是由于事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型。
addEventListener()
W3C给浏览器提供了一个函数 - addEventListener
- 事件绑定API
IE5*:baba.attachEvent('onclick',fn)
//冒泡
网景:baba.addEventListener('click',fn)
//捕获
W3C:baba.addEventListener('click',fn,bool)
- 如果
bool
不传或为falsy
就让fn
走冒泡,即当前浏览器在冒泡阶段发现baba
有fn
监听函数,就会调用fn
,并提供事件信息 - 如果
bool
为true
就让fn
走捕获,即当浏览器在捕获阶段发现baba
有fn
监听函数,就会调用fn
,并提供事件信息
事件委托
冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。
举例1:
你要给多个按钮添加点击事件
<div id="div1">
<button>click 1</button>
<button>click 2</button>
<button>click 3</button>
<button>click 4</button>
<button>click 5</button>
<button>click 6</button>
<button>click 7</button>
<button>click 8</button>
<button>click 9</button>
</div>
监听这些按钮的祖先,等冒泡的时候判断target
是不是这写按钮中的一个
div.addEventListener('click',(e)=>{
const t = e.target
if(t.tagName.toLowerCase() === 'button'){
console.log('button 被点击了')
}
})
举例2:
你要监听目前不存在的元素的点击事件
<div id="div1">
</div>
监听祖先元素,等点击的时候看看是不是我想要监听的元素即可
setTimeout(()=>{
const button = document.creatElement('button')
button.textContent = 'click 1'
div1.appendChild(button)
},1000)
div1.addEventListener('click',(e)=>{
const t =e.target
if(t.tagName.toLowerCase() === 'button'){
console.log('button 被 click')
}
})
封装事件委托
function on(eventType,element,selector, fn){
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(evenType,(e)=>{
const t = e.targer
if(t.matches(selector)){
fn(e)
}
})
}