多个 addEventListener 如何在 JavaScript 中工作?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16273635/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 04:00:54  来源:igfitidea点击:

How do multiple addEventListener work in JavaScript?

javascriptjavascript-eventsaddeventlistener

提问by user1643156

There are 2 scripts in a document

一个文档中有 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;
};

To make sure my click event does not get overridden by other script, I switched to addEventListener.

为了确保我的点击事件不会被其他脚本覆盖,我切换到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);

Now I got another question. Since return falsein the second code is defined after alert, how come it does not prevent alert from being called?

现在我有另一个问题。既然return false第二个代码是在 之后定义的alert,怎么不阻止alert被调用呢?

What if I want my script to get total control of click event (like return false all the time disregarding events defined in other scripts)?

如果我希望我的脚本完全控制单击事件(例如始终返回 false,而忽略其他脚本中定义的事件),该怎么办?

回答by T.J. Crowder

What if I want my script to get total control of click event (like return false all the time disregarding events defined in other scripts)?

如果我希望我的脚本完全控制单击事件(例如始终返回 false,而忽略其他脚本中定义的事件),该怎么办?

If you can register your handler first, before they do, you can do that, provided the browser you're using correctly implements DOM3 events (which it probably does unless it's IE8 or earlier).

如果您可以先注册您的处理程序,那么您就可以这样做,前提是您使用的浏览器正确实现了 DOM3 事件(除非是 IE8 或更早版本,否则它可能会这样做)。

There are (at least) four things involved here:

这里涉及(至少)四件事:

  1. Preventing the default.

  2. Stopping propagation to ancestor elements.

  3. Stopping other handlers on the sameelement from being called.

  4. The order in which handlers are called.

  1. 防止默认。

  2. 停止传播到祖先元素。

  3. 停止调用同一元素上的其他处理程序。

  4. 调用处理程序的顺序。

In order:

为了:

1. Preventing the default

1. 防止默认

This is what return falsefrom a DOM0 handler does. (Details: The Story on Return False.) The equivalent in DOM2 and DOM3 is preventDefault:

这就是return falseDOM0 处理程序所做的。(详细信息:返回 False 的故事。)DOM2 和 DOM3 中的等价物是preventDefault

document.addEventListener("click", function(e) {
    e.preventDefault();
}, false);

Preventing the default may not be all that relevant to what you're doing, but since you were using return falsein your DOM0 handler, and that prevents the default, I'm including it here for completeness.

防止默认设置可能与您正在做的事情无关,但是由于您return false在 DOM0 处理程序中使用,并且这会阻止默认设置,为了完整性,我将其包含在此处。

2. Stopping propagation to ancestor elements

2.停止传播到祖先元素

DOM0 handlers have no way to do this. DOM2 ones do, via stopPropagation:

DOM0 处理程序无法做到这一点。DOM2 的,通过stopPropagation

document.addEventListener("click", function(e) {
    e.stopPropagation();
}, false);

But stopPropagationdoesn't stop other handlers on that same element getting called. From the spec:

stopPropagation不会停止调用同一元素上的其他处理程序。从规范

The stopPropagationmethod is used prevent further propagation of an event during event flow. If this method is called by any EventListenerthe event will cease propagating through the tree. The event will complete dispatch to all listeners on the current EventTargetbefore event flow stops.

stopPropagation方法用于防止事件流期间事件的进一步传播。如果任何人调用此方法,则EventListener事件将停止在树中传播。事件将EventTarget在事件流停止之前完成对当前所有侦听器的分派。

(My emphasis.)

(我的重点。)

3. Stopping other handlers on the sameelement from being called

3. 停止调用同一元素上的其他处理程序

Naturally, this didn't come up for DOM0, because there couldn't beother handlers for the same event on the same element. :-)

自然,这不会出现在 DOM0 上,因为在同一元素上不可能其他处理程序来处理同一事件。:-)

As far as I'm aware, there's no way to do this in DOM2, but DOM3 gives us stopImmediatePropagation:

据我所知,在 DOM2 中没有办法做到这一点,但 DOM3 给了我们stopImmediatePropagation

document.addEventListener("click", function(e) {
    e.stopImmediatePropagation();
}, false);

Some libraries offer this feature (even on non-DOM3 systems like IE8) for handlers hooked up via the library, see below.

一些库为通过库连接的处理程序提供此功能(甚至在非 DOM3 系统上,如 IE8),见下文。

4. The order in which handlers are called

4. 处理程序的调用顺序

Again, not something that related to DOM0, because there couldn't be other handlers.

同样,与 DOM0 无关,因为不可能有其他处理程序。

In DOM2, the specification explicitlysays that the order in which the handlers attached to an element are called is not guaranteed; but DOM3 changes that, saying that handlers are called in the order in which they're registered.

在 DOM2 中,规范明确表示不保证调用附加到元素的处理程序的顺序;但是 DOM3 改变了这一点,说处理程序按照它们注册的顺序被调用。

First, from DOM2 Section 1.2.1:

首先,来自 DOM2 Section 1.2.1

Although all EventListenerson the EventTargetare guaranteed to be triggered by any event which is received by that EventTarget, no specification is made as to the order in which they will receive the event with regards to the other EventListenerson the EventTarget.

虽然所有EventListenersEventTarget都保证通过由接收到的任何事件触发EventTarget,不规范作出了它们将与问候其他接收事件的顺序EventListenersEventTarget

But this is superceded by DOM3 Section 3.1:

但这被 DOM3第 3.1 节取代:

Next, the implementation must determine the current target's candidate event listeners. This must be the list of all event listeners that have been registered on the current target in their order of registration.

接下来,实现必须确定当前目标的候选事件侦听器。这必须是按注册顺序在当前目标上注册的所有事件侦听器的列表。

(My emphasis.)

(我的重点。)

Some libraries guarantee the order, provided you hook up the events with the library.

一些库可以保证顺序,前提是您将事件与库连接起来。

It's also worth noting that in Microsoft's predecessor to DOM2 (e.g., attachEvent), it was the opposite of DOM3's order: The handlers were called in reverseorder of registration.

还值得注意的是,在 Microsoft DOM2 的前身(例如attachEvent)中,它与 DOM3 的顺序相反:以注册的相反顺序调用处理程序。



So taking #3 and #4 together, if you can register your handler first, it will get called first, and you can use stopImmediatePropagationto prevent other handlers getting called. Provided the browser implements DOM3 correctly.

因此,将 #3 和 #4 放在一起,如果您可以先注册您的处理程序,它将首先被调用,您可以使用它stopImmediatePropagation来防止其他处理程序被调用。前提是浏览器正确实现了 DOM3。



All of this (including the fact that IE8 and earlier don't even implement DOM2 events, much less DOM3) is one reason people use libraries like jQuery, some of which do guarantee the order (as long as everything is hooking up their handlers via the library in question) and offer ways to stop even other handlers on the same element getting called. (With jQuery, for instance, the order is the order in which they were attached, and you can use stopImmediatePropagationto stop calls to other handlers. But I'm not trying to sell jQuery here, just explaining that some libs offer more functionality than the basic DOM stuff.)

所有这些(包括 IE8 及更早版本甚至不实现 DOM2 事件,更不用说 DOM3)是人们使用像 jQuery 这样的库的原因之一,其中一些库确实保证了顺序(只要一切都通过有问题的库)并提供方法来停止调用同一元素上的其他处理程序。(例如,对于 jQuery,顺序是它们被附加的顺序,您可以使用它stopImmediatePropagation来停止对其他处理程序的调用。但我不想在这里推销 jQuery,只是解释一些库提供的功能比基本的 DOM 东西。)