回想起自己学习事件模式那会儿,怎么都记不住事件的传播流程和 addEventListener 的设置细节,后来发现好的图片更能能帮助我们理解逻,所以做了一个 GIF。
事件传播模式
假设我们现在有三个DOM
节点,并且假设 div 为根节点。(通常事件的捕获会从根节点开始)
然后我们为这些DOM
节点设置好监听。
// 设置第三个参数为 true 则在捕获阶段触发
div.addEventListener('click', null, true);
p.addEventListener('click', null, true);
span.addEventListener('click', null, true);
span.addEventListener('click', null);
p.addEventListener('click', null);
div.addEventListener('click', null);
现在我们点击span
,那么click
事件会被触发,事件会从div
进入捕获阶段,从父级向子级传递,到达事件目标后进入冒泡阶段,从子级像父级传递。
(click)span
=> div -> p -> span -> span -> p -> div
如果我们点击p
,那么事件目标为p
元素,事件从div
元素开始捕获,并从p
元素处折返变为冒泡。
(click)p
=> div -> p -> p -> div
多次绑定
如果在一个节点上多次绑定同一个事件的监听,它们会按照事件传播流程进行(先捕获后冒泡),如果所处流程一样则按照先绑定先触发的原则。
// 设置第三个参数为 true 则在捕获阶段触发
div.addEventListener('click', null, true) // #1
div.addEventListener('click', null) // #2
div.addEventListener('click', null, true) // #3
p.addEventListener('click', null, true);
p.addEventListener('click', null);
(click)p
=> div#1 -> div#3 -> p -> p -> div#2
虽然申明的顺序是div#1
div#2
div#3
,但是捕获先于冒泡,所以 div#1
div#3
依次触发,而div#2
在冒泡过程中触发。
stopPropagation
这是Event
对象的一个方法,用来阻止事件进一步传播。
// 设置第三个参数为 true 则在捕获阶段触发
// #1
div.addEventListener('click', function (event) {
event.stopPropagation();
}, true)
div.addEventListener('click', null) // #2
div.addEventListener('click', null, true) // #3
p.addEventListener('click', null, true);
p.addEventListener('click', null);
(click)p
=> div#1 -> div#3
使用了stopPropagation()
之后,事件就不能进一步传播了,即使是在div
上,捕获和冒泡被认为是两个步骤,所以在捕获阶段传播被阻止时同节点上的冒泡也不会触发。
stopImmediatePropagation
这是Event
对象的一个方法,一旦调用这个方法,则该元素上未触发的监听都不会被触发,事件也不会进一步传播。
现在我们在div
上再多增加一个事件监听,并把stopImmediatePropagation
添加在捕获事件中第二个触发的监听上。
// 设置第三个参数为 true 则在捕获阶段触发
div.addEventListener('click', null, true) // #1
div.addEventListener('click', null) // #2
// #3
div.addEventListener('click', function (e) {
e. stopImmediatePropagation();
}, true)
div.addEventListener('click', null, true) // #4
p.addEventListener('click', null, true);
p.addEventListener('click', null);
(click)p
=> div#1 -> div#3
使用了stopImmediatePropagation()
之后,连当前节点中等待触发的监听都沉默了。
扩展阅读
在比较新的浏览器中,addEventListener
支持更多参数配置,第三个参数类型支持object
。
target.addEventListener(type, listener[, options]);
这个 options 支持三个字段
- capture
Boolean
是否在捕获模式触发 - once
Boolean
是否仅触发一次 - passive
Boolean
是否使用被动模式
这里展示一下once
的效果
// 设置第三个参数为 true 则在捕获阶段触发
div.addEventListener('click', null, true);
p.addEventListener('click', null, true);
span.addEventListener('click', null, true);
span.addEventListener('click', null);
p.addEventListener('click', null, {once: true}); // 在冒泡阶段只触发一次
div.addEventListener('click', null);
(click)span
=> div -> p -> span -> span -> p -> div
(click)span
=> div -> p -> span -> span -> div
Demo
罗小黑写写文字
如果喜欢文章 请留下一个赞~
如果喜欢文章 分享给更多人~自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
转载时请保留原文链接 以保证可及时获取对文章的订正和修改