事件在web
开发中太常见了。
web
浏览器在主线程主队列任务执行完成之后。
基本就进入到了到了eventLoop
等待事件驱动的环节了。
1. 事件模型
W3C 标准定义了两种事件模型.
- 事件捕获
- 事件冒泡
除了 IE9 以及以下版本,现在主流的浏览器都支持这两种事件模型.
事件捕获:
在由嵌套层次(非视觉上)的元素结构里.
当有事件发生时,事件总是被最外层的元素捕获到,并依次往内部传递.
传递方向是:从外到内.
<div class="parent">
<div class="child"></div>
</div>
let parent = document.querySelector('.parent'),
child = document.querySelector('.child')
parent.addEventListener('click', function () {
console.log('parent event capture')
}, true)
child.addEventListener('click', function () {
console.log('child event capture')
}, true)
结果:
parent event capture
child event capture
事件冒泡
在包含有嵌套关系层级结构的元素里.
当有事件发生时,事件总是先被点击的那个元素获取到.
然后依次往外传递.
事件传递方向:从内到外.
<div class="parent">
<div class="child"></div>
</div>
parent.addEventListener('click', function () {
console.log('parent event bubble')
}, false)
child.addEventListener('click', function () {
console.log('child event bubble')
}, false)
结果:
child event bubble
parent event bubble
自己提问一:如果同一个元素同时绑定事件冒泡和事件捕获两种方式呢?
先绑定事件冒泡,在绑定事件捕获.
let bubble_capture = document.querySelector('.bubble-capture')
bubble_capture.addEventListener('click', function () {
console.log('事件冒泡')
}, false)
bubble_capture.addEventListener('click', function () {
console.log('事件捕获')
}, true)
执行结果:
事件冒泡
事件捕获
先绑定事件捕获,在绑定事件冒泡.
bubble_capture.addEventListener('click', function () {
console.log('事件捕获')
}, true)
bubble_capture.addEventListener('click', function () {
console.log('事件冒泡')
}, false)
执行结果:
事件捕获
事件冒泡
先执行事件捕获,在执行事件冒泡.
结论:
单个元素同时绑定两种事件模型.先绑定的模型先执行,后绑定的模型后执行.
自己提问二:如果带有嵌套关系的多个元素同时绑定事件冒泡和事件捕获两种方式呢?
WEB页面中,有三个嵌套的DIV元素.
<div class="parent">
<div class="child">
<div class="box"></div>
</div>
</div>
let parent = document.querySelector('.parent'),
child = document.querySelector('.child'),
box = document.querySelector('.box')
// 事件捕获.
parent.addEventListener('click', function () {
console.log('parent event capture')
}, true)
child.addEventListener('click', function () {
console.log('child event capture')
}, true)
box.addEventListener('click', function () {
console.log('box event capture')
}, true)
// 事件冒泡
parent.addEventListener('click', function () {
console.log('parent event bubble')
}, false)
child.addEventListener('click', function () {
console.log('child event bubble')
}, false)
box.addEventListener('click', function () {
console.log('box event bubble')
}, false)
点击最里面的蓝色的DIV.(box)
输出结果:
parent event capture
child event capture
box event capture
box event bubble
child event bubble
parent event bubble
打乱事件模型绑定代码:
box.addEventListener('click', function () {
console.log('box event bubble')
}, false)
parent.addEventListener('click', function () {
console.log('parent event capture')
}, true)
box.addEventListener('click', function () {
console.log('box event capture')
}, true)
child.addEventListener('click', function () {
console.log('child event bubble')
}, false)
parent.addEventListener('click', function () {
console.log('parent event bubble')
}, false)
child.addEventListener('click', function () {
console.log('child event capture')
}, true)
输出结果:
parent event capture
child event capture
box event bubble
box event capture
child event bubble
parent event bubble
结论:
当多个嵌套关系的元素同时绑定了事件冒泡和事件捕获两种事件模型时.
除了触发事件的那个元素(这里是最内部的蓝色DIV)执行顺序和模型绑定顺序一致意外.
其他外层的元素事件模型执行顺序永远都是 事件捕获 优先于 事件冒泡 执行.
所以,在某种程度上,我们可以理解为:事件捕获 优先于 事件冒泡 执行.