如何提升window.addEventListener事件监听器的执行优先级
在浏览器事件机制中,window.addEventListener 用于为 window 对象绑定事件监听器,所有绑定的监听器会按照特定的顺序执行。许多开发者希望调整监听器的执行优先级,让某些关键逻辑优先运行,下面就来详细讲解相关的实现方法和原理。
一、浏览器事件监听器的默认执行顺序
当使用 addEventListener 绑定同一事件类型时,监听器的执行顺序遵循以下规则:
所有非捕获阶段的监听器(第三个参数为
false或默认未传递)按照绑定的先后顺序依次执行所有捕获阶段的监听器(第三个参数为
true)按照绑定的先后顺序依次执行,且捕获阶段监听器会先于非捕获阶段监听器执行
我们可以通过以下示例代码验证这个顺序:
// 先绑定非捕获阶段监听器A
window.addEventListener('click', function() {
console.log('非捕获监听器A,先绑定');
});
// 后绑定非捕获阶段监听器B
window.addEventListener('click', function() {
console.log('非捕获监听器B,后绑定');
});
// 绑定捕获阶段监听器C
window.addEventListener('click', function() {
console.log('捕获监听器C');
}, true);
// 绑定捕获阶段监听器D
window.addEventListener('click', function() {
console.log('捕获监听器D,后绑定捕获监听器');
}, true);点击页面后,控制台输出顺序为:
捕获监听器C 捕获监听器D,后绑定捕获监听器 非捕获监听器A,先绑定 非捕获监听器B,后绑定
二、提升执行优先级的可行方法
1. 使用捕获阶段绑定监听器
由于捕获阶段的监听器会先于所有非捕获阶段的监听器执行,因此如果希望某个监听器的逻辑优先运行,可以将其绑定为捕获阶段,即给 addEventListener 的第三个参数传递 true。
示例:
// 关键逻辑,设置为捕获阶段,优先执行
window.addEventListener('click', function() {
console.log('优先执行的关键逻辑');
}, true);
// 普通逻辑,非捕获阶段
window.addEventListener('click', function() {
console.log('普通逻辑,后执行');
});点击页面后,优先执行的关键逻辑会先输出,满足提升优先级的需求。
2. 提前绑定监听器
如果是同为非捕获阶段的监听器,执行顺序和绑定顺序一致,因此可以在其他同类型监听器之前完成绑定,让该监听器优先执行。
示例:
// 提前绑定的监听器,优先执行
window.addEventListener('click', function() {
console.log('提前绑定的监听器,优先执行');
});
// 后续其他逻辑中绑定的监听器,后执行
setTimeout(function() {
window.addEventListener('click', function() {
console.log('延迟绑定的监听器,后执行');
});
}, 0);3. 移除后重新插入关键监听器
如果监听器已经绑定,需要临时提升某个已存在监听器的优先级,可以先移除该监听器,再重新绑定,这样它会排到同类型监听器队列的最后位置,在事件触发时最后执行?不对,是重新绑定后会在同类型队列的末尾,下次触发时会在同类型监听器里最后执行?哦不对,我们要的是优先,所以如果是要优先,应该如果是已经绑定的,想要它优先,那应该如果是捕获阶段的话已经在最前,如果是非捕获的,想要优先,那可以把原来的移除,然后如果是非捕获的,重新绑定后会在末尾,反而更靠后?不对,我刚才搞错了,非捕获阶段是按绑定顺序来,先绑定的先执行,所以如果是已经绑定了一个非捕获监听器A,后来又绑定了B,A先执行,B后执行。如果现在希望B比A先执行,那可以把A移除,重新绑定A,这样A就到B后面了?不对,应该是:原来的顺序是A(先绑非捕获)、B(后绑非捕获),执行顺序是A先,B后。如果要让B先执行,A后执行,那么可以先移除B,再重新绑定B,这时候B的绑定顺序就变成在A之后?不对啊,那B还是在A后面?哦不对,我搞反了,先绑定的先执行,所以A先绑,A先执行。如果B是后绑的,B后执行。如果想要B先执行,那应该让B的绑定时间在A之前。所以如果已经绑了A,现在要让B比A先执行,那可以移除A,然后绑定B,再绑定A,这样B先绑,A后绑,执行顺序就是B先,A后。对,所以这个方法适用于已经存在多个监听器,需要调整顺序的场景。
示例:
function listenerA() {
console.log('监听器A');
}
function listenerB() {
console.log('监听器B');
}
// 初始绑定顺序:A先,B后
window.addEventListener('click', listenerA);
window.addEventListener('click', listenerB);
// 执行顺序:A先,B后
// 现在希望B先执行,A后执行,操作如下:
// 1. 移除两个监听器
window.removeEventListener('click', listenerA);
window.removeEventListener('click', listenerB);
// 2. 先绑定B,再绑定A
window.addEventListener('click', listenerB);
window.addEventListener('click', listenerA);
// 重新触发事件时,执行顺序变为B先,A后三、注意事项
捕获阶段的监听器虽然执行更早,但需要注意事件传播的方向:捕获阶段是从
window向下传播到目标元素,非捕获(冒泡)阶段是从目标元素向上传播到window,如果事件在传播过程中被stopPropagation阻止,后续的监听器可能不会执行。addEventListener的第三个参数除了布尔值,还可以是对象,其中capture属性可以设置是否为捕获阶段,once设置是否只执行一次,passive设置是否忽略preventDefault,这些属性不会影响执行优先级的基本规则。不要试图通过修改监听器函数的内部逻辑来提升优先级,事件监听器的执行顺序由浏览器事件机制控制,和函数本身的内容无关。
四、总结
提升 window.addEventListener 监听器执行优先级的核心思路是:要么利用捕获阶段优先于冒泡阶段的规则,将关键监听器绑定为捕获阶段;要么调整同类型监听器的绑定顺序,让关键监听器先被绑定;如果是已绑定的监听器,可以通过移除后重新按目标顺序绑定的方式调整优先级。根据实际场景选择合适的方法即可。