DOM 事件机制
有三个 DIV 元素, #grandfather 包含 #father 包含 #child,并给三个 DIV 绑定一个点击事件,输出一段文字,当我们点击最内层的文字,谁的文字会最先打出来呢?
<div id="grandfather">
<div id="father">
<div id="child">请点击我</div>
</div>
</div>
<script>
let grandfather = document.querySelector('#grandfather');
let father = document.querySelector('#father');
let child = document.querySelector('#child');
grandfather.onclick=()=>{
console.log('grandfather 被点击')
}
father.onclick=()=>{
console.log('father 被点击')
}
child.onclick=()=>{
console.log('child 被点击')
}
</script>
IE5认为先执行 #child 中的函数的这种 从内到外 的顺序,叫做事件冒泡
网景认为先执行 #grandfather 中的函数的这种 从外到内 的顺序,叫做事件捕获
于是 W3C发布标准: 首先 按照 从外到内 的顺序找监听函数,然后 按照 从内到外 的顺序找监听函数,如果找到监听函数就调用,并提供事件信息,没有就跳过
// 事件绑定 API
IE5: child.attachEvent('onclick',fn) //事件冒泡
网景: child.addEventListener('click',fn) //事件捕获
W3C: child.addEventListener('click',fn,bool)
W3C在 网景的事件绑定API 的基础上,添加了第三个参数bool,当 bool 没有传值或者值为false时,就让 fn 走事件冒泡;当 bool 值为 true 时,就让 fn 走事件捕获
注意:
事件捕获不可取消,但是事件冒泡可以用e.stopPropagation()中断冒泡.scroll不能够取消冒泡,阻止默认事件也没用,要先阻止wheel和touchstart的默认动作Bubbles表示是否冒泡Cancelable表示是否支持开发者取消冒泡
事件委托
事件委托 利用 DOM 元素冒泡的事件机制,把需要绑定在子元素的响应事件委托给父元素,让父元素进行监听
举个例子:
比如有素描课程,班里每个人都需要买素描纸,铅笔等工具.这个时候可以个人去买个人的,但最好的办法就是派班长去帮所有人买,买回来再分给所有人.这样大的数量可以讲价,节省一点钱,而且老板对班长有印象,下次去买还可以便宜点.
例子中的买工具就是一个事件,每个同学是子元素,班长就是父元素,同学委托班长帮忙买工具就是事件委托,节省钱和不用精力,有人要再买时,班长可以在帮忙.同样的,在代码中的事件委托可以减少每个子元素事件的注册,大量节省内存,而且当新增子对象时无需再次对其绑定.
可以看出 事件委托 的好处:
- 减少每个子元素事件的注册,大量节省内存
- 新增子对象时无需再次对其绑定
基本实现:
点击 li 可以输出对应的 innerHTML
HTML:
<ul id="ulList">
<li>li 1</li>
<li>li 2</li>
<li>li 3</li>
<li>li 4</li>
<li>li 5</li>
<li>li 6</li>
</ul>
JS:
on("click", "#ulList", "li", (e) => {
console.log(e.target.innerHTML);
});
//封装事件委托函数
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelector(element);
}
element.addEventListener(eventType, (e) => {
const t = e.target;
if (t.matches(selector)) {
fn(e);
}
});
}