事件流之事件冒泡与事件捕获
在浏览器发展的过程中,开发团队遇到了一个问题。那就是页面中的哪一部分拥有特定的事件?
可以想象画在一张纸上的一组同心圆,如果你把手指放在圆心上,那么你的手指指向的其实不是一个圆,而是纸上所有的圆。放到实际页面中就是,你点击一个按钮,事实上你还同时点击了按钮所有的父元素。
开发团队的问题就在于,当点击按钮时,是按钮最外层的父元素先收到事件并执行,还是具体元素先收到事件并执行?所以这儿引入了事件流的概念
事件流定义
事件流所描述的就是从页面中接受事件的顺序。
因为有两种观点,所以事件流也有两种,分别是事件冒泡和事件捕获。现行的主流是事件冒泡。
事件冒泡`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event Bubbling</title>
</head>
<body>
<button id="clickMe">Click Me</button>
</body>
</html>
<!-- 然后,我们给button和它的父元素,加入点击事件。 -->
var button = document.getElementById('clickMe');
button.onclick = function() {
console.log('1. You click Button');
};
document.body.onclick = function() {
console.log('2. You click body');
};
document.onclick = function() {
console.log('3. You click document');
};
window.onclick = function() {
console.log('4. You click window');
};
效果如下
在代码所示的页面中,如果点击了button,那么这个点击事件会按如下的顺序传播(Chrome浏览器):
1.button
2.body
3.document
4.window
也就是说,click事件首先在元素上发生,然后逐级向上传播。这就是事件冒泡。
事件捕获
事件捕获的概念,与事件冒泡正好相反。它认为当某个事件发生时,父元素应该更早接收到事件,具体元素则最后接收到事件。比如说刚才的demo,如果是事件捕获的话,事件发生顺序会是这样的:
1.window
2.document
3.body
4.button
当然,由于时代更迭,事件冒泡方式更胜一筹。所以放心的使用事件冒泡,有特殊需要再使用事件捕获即可。
DOM事件流
DOM事件流包括三个阶段。
1.事件捕获阶段
2.处于目标阶段
3.事件冒泡阶段
1.事件捕获阶段
也就是说,当事件发生时,首先发生的是事件捕获,为父元素截获事件提供了机会。
例如,我把上面的Demo中,window点击事件更改为使用事件捕获模式。(addEventListener最后一个参数,为true则代表使用事件捕获模式,我的理解也就是把事假绑定在了事件捕获阶段,false则表示使用事件冒泡模式,把事件绑定在了事件冒泡阶段。不理解的可以去学习一下addEventListener函数的使用)
window.addEventListener('click', function() {
console.log('4. You click window');
}, true);
2.处于目标与事件冒泡阶段
事件到了具体元素时,在具体元素上发生,并且被看成冒泡阶段的一部分。
随后,冒泡阶段发生,事件开始冒泡。
3.事件冒泡阶段
事件冒泡过程,是可以被阻止的。防止事件冒泡而带来不必要的错误和困扰。
这个方法就是:stopPropagation()
我们对button的click事件做一些改造。
button.addEventListener('click', function(event) {
// event为事件对象
console.log('1. You click Button');
event.stopPropagation();
console.log('Stop Propagation!');
}, false);
值得注意是: IE9开始 IE11 edge :addEventListener
IE9 之前 :attachEvent/detachEvent
1.进行事件类型传参需要带上on前缀;
2.这种方式只支持事件冒泡,不支持事件捕获
所以事件在到达具体元素后,停止了冒泡。但不影响父元素的事件捕获。
总结与感想
事件流:描述的就是从页面中接受事件的顺序。
DOM事件流的三个阶段:
1.事件捕获阶段----->2.处于目标阶段------->3.事件冒泡阶段
在这个过程中可以使用event.stopPropagation();来阻止这个过程继续执行;
而addEventListener()的第三个参数为:
true则表示将事件绑定在捕获阶段,false表示将事件绑定在冒泡阶段;