多个 addEventListener 如何在 JavaScript 中工作?
Posted
技术标签:
【中文标题】多个 addEventListener 如何在 JavaScript 中工作?【英文标题】:How do multiple addEventListener work in JavaScript? 【发布时间】:2013-04-22 19:45:46 【问题描述】:一个文档中有2个脚本
// my_script.js goes first
document.onclick = function()
alert("document clicked");
;
// other_script.js comes after
// this overrides the onclick of my script,
// and alert will NOT be fired
document.onclick = function()
return false;
;
为了确保我的点击事件不会被其他脚本覆盖,我切换到addEventListener
。
// my_script.js goes first
document.addEventListener("click", function()
alert("document clicked");
, false);
// other_script.js comes after
document.addEventListener("click", function()
return false;
, false);
现在我有另一个问题。既然第二个代码中的return false
是在alert
之后定义的,为什么不阻止alert的调用呢?
如果我希望我的脚本完全控制点击事件(比如始终返回 false,而忽略其他脚本中定义的事件)怎么办?
【问题讨论】:
【参考方案1】:如果我希望我的脚本完全控制点击事件(比如始终返回 false,而忽略其他脚本中定义的事件)怎么办?
如果您可以先注册您的处理程序,在他们注册之前,您可以这样做,前提是您使用的浏览器正确实现了 DOM3 事件(除非它是 IE8 或更早版本,否则它可能会这样做)。
这里(至少)涉及四件事:
防止默认。
停止传播到祖先元素。
停止调用 same 元素上的其他处理程序。
处理程序的调用顺序。
按顺序:
1。防止默认
这就是来自 DOM0 处理程序的 return false
所做的。 (详情:The Story on Return False。)DOM2 和 DOM3 中的等价物是preventDefault
:
document.addEventListener("click", function(e)
e.preventDefault();
, false);
防止默认值可能与您正在做的事情无关,但由于您在 DOM0 处理程序中使用了return false
,这会阻止默认值,为了完整起见,我将其包含在此处。
2。停止传播到祖先元素
DOM0 处理程序无法做到这一点。 DOM2 的,通过stopPropagation
:
document.addEventListener("click", function(e)
e.stopPropagation();
, false);
但stopPropagation
不会阻止调用同一元素上的其他处理程序。来自the spec:
stopPropagation
方法用于防止在事件流期间进一步传播事件。如果任何EventListener
调用此方法,则事件将停止在树中传播。 事件将在事件流停止之前完成向当前EventTarget
上的所有侦听器的调度。
(我的重点。)
3。停止调用 same 元素上的其他处理程序
当然,DOM0 不会出现这种情况,因为对于同一元素上的同一事件,不可能有 其他处理程序。 :-)
据我所知,在 DOM2 中没有办法做到这一点,但 DOM3 给了我们stopImmediatePropagation
:
document.addEventListener("click", function(e)
e.stopImmediatePropagation();
, false);
一些库为通过库连接的处理程序提供此功能(甚至在 IE8 等非 DOM3 系统上),见下文。
4。调用处理程序的顺序
同样,与 DOM0 无关,因为不可能有其他处理程序。
在 DOM2 中,规范明确说,附加到元素的处理程序被调用的顺序并不能保证;但是 DOM3 改变了这一点,表示处理程序按照它们注册的顺序被调用。
首先,来自 DOM2 Section 1.2.1:
尽管
EventTarget
上的所有EventListeners
都保证会被该EventTarget
接收到的任何事件触发,但没有指定它们接收事件的顺序相对于另一个EventListeners
EventTarget
。
但这已被 DOM3 Section 3.1 取代:
接下来,实现必须确定当前目标的候选事件侦听器。这必须是按注册顺序在当前目标上注册的所有事件侦听器的列表。
(我的重点。)
如果您将事件与库挂钩,一些库会保证顺序。
还值得注意的是,在 Microsoft 的 DOM2 前身(例如,attachEvent
)中,它与 DOM3 的顺序相反:处理程序以 reverse 的注册顺序调用。
所以把#3 和#4 放在一起,如果你可以先注册你的处理程序,它会先被调用,你可以使用stopImmediatePropagation
来防止其他处理程序被调用。前提是浏览器正确实现了 DOM3。
所有这些(包括 IE8 和更早版本甚至不实现 DOM2 事件,更不用说 DOM3)是人们使用 jQuery 之类的库的原因之一,其中一些确实保证了顺序(只要一切都在连接他们的处理程序通过有问题的库),并提供了阻止同一元素上的其他处理程序被调用的方法。 (例如,对于 jQuery,顺序是它们附加的顺序,您可以使用 stopImmediatePropagation
来停止对其他处理程序的调用。但我不想在这里出售 jQuery,只是解释一些库提供更多功能比基本的 DOM 东西。)
【讨论】:
好吧,我在发帖之前实际上已经尝试过(preventDefault
,还有stopPropagation
)。警报仍然会被调用。
我将最后一段代码 (addEventListener ... return false) 封装在一个 setTimeout (3000) 中,这应该确保它在 alert() 之后被添加,但是仍然会调用 alert。
@user1643156:同样:不能保证调用处理程序的顺序。另外,据我所知,没有办法通过 DOM2 停止调用同一元素上的其他处理程序(stopPropagation
停止将事件转到 ancestor 元素)。
我将警报更改为 console.log。我一直在点击页面,试图得到一个随机异常,因为你引用了not guaranteed
,所以首先调用return fasle
(我理解这是随机的),但幸运的是,console.log 总是首先被调用。
谢谢 T.J.克劳德,e.stopImmediatePropagation();
工作。但它必须在任何其他事件监听器之前定义。并且处理程序的执行顺序确实是从上到下,至少在我的测试中没有你引用not guaranteed
的那么多。感谢您提供详细信息。以上是关于多个 addEventListener 如何在 JavaScript 中工作?的主要内容,如果未能解决你的问题,请参考以下文章