The Order of Multiple Event Listeners
Asked Answered
F

4

24

I've come across an oddity while using Prototype to handle click events. If you click the button in the code below, it will trigger three alerts: 'Click 1', 'Click 2' and 'Click 3'. Modern browsers will invoke the listeners in the order they are registered in, while IE8 (and perhaps older IE versions as well) will invoke them in the opposite order. I find this odd because I thought Prototype maintained and executed a queue of listeners, which should be browser independent. Is this not so? If not, are event listeners supposed to be run in a certain order or are they asynchronous and thus their order irrelevant?

    <button id="button">Click me</button>
    <script type="text/javascript">
        $('button').observe('click', function(event) {
            alert('Click 1');
        });
        $('button').observe('click', function(event) {
            alert('Click 2');
        });
        $('button').observe('click', function(event) {
            alert('Click 3');
        });
    </script>
Fluoridate answered 1/3, 2012 at 7:58 Comment(1)
Does this answer your question? Are JavaScript DOM event handlers called in order of registration?Castellanos
T
36

Prototype relies on the browser's underlying firing mechanism for order (not all libraries do, see below). The order in which event handlers are fired was not guaranteed by the DOM events stuff originally (but keep reading). From the DOM2 Events specification:

Although all EventListeners on the EventTarget are 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 EventListeners on the EventTarget.

The vast majority of browser implementations (Chrome, Firefox, Opera, etc.), including IE9, fire the handlers in the order in which they were attached. IE8 and earlier do it the other way around.

The newer (and now very well-established) DOM3 event spec added the requirement that they be fired in order of registration (what most browsers do):

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.

...which is probably part of why IE9 does that now (IE9 markedly improved Microsoft's support for the events standards, adding addEventListener, etc.).

Some JavaScript libraries (jQuery for instance) do guarantee the order regardless of the browser, by attaching only a single handler per event per element and maintaining their own list of user code handlers to fire.

Ticino answered 1/3, 2012 at 8:9 Comment(0)
E
1

As a formatted comment of @T.J. Crowder's answer, I tested which modern browsers actually trigger the listeners in registration order.

2016-10-06: The results are that all following browsers do: chrome 53, firefox 49, safari 9, opera 40, ie 11 and edge 13 via virtualbox on mac host.

The code of my test can be found here: https://github.com/lingtalfi/browsers-behaviours/blob/master/listeners-execution-order/listeners.md

Eger answered 6/10, 2016 at 6:27 Comment(0)
D
1

In addition to what has already been discussed, I just wanted to point out that there's a difference in the order different browsers handle the onclick property of the elements when other event listeners are registered. As far as I have been able to test:

  • Firefox treats the onclick as another event listener, and executes the callback in the order it has been set.
  • Chrome executes the onclick callback AFTER all registered listeners.
  • IE8 executes the onclick callback BEFORE all registered listeners (which are executed in reverse order as stated in other answers).

As a result, in the following example, when the button is clicked:

<!DOCTYPE html>
<html>
<script>
function ini() {
    let btn = document.getElementById("btn");
    if (btn.addEventListener) {
        btn.addEventListener("click", func1, true);
    } else {
        btn.attachEvent("onclick", func1);
    }
    btn.onclick=func2;
    if (btn.addEventListener) {
        btn.addEventListener("click", func3, true);
    } else {
        btn.attachEvent("onclick", func3);
    }
}

function func1() {
    console.log("Called 1");
}
function func2() {
    console.log("Called 2");
}
function func3() {
    console.log("Called 3");
}
</script>
<body onload="ini();">
<input type="button" id="btn" value="button">
</body>
</html>

Firefox will show:

Called 1
Called 2
Called 3

Chrome will show:

Called 1
Called 3
Called 2

And IE8 will show:

Called 2
Called 3
Called 1
Dogvane answered 19/12, 2022 at 16:4 Comment(0)
L
0

According to this bug report, it seems to be an existing bug, and that it is in fact browser dependent.

To summarize the report:

It's browser dependent, but some users (like the poster) expect the events to fire in order. The bug entry was changed at one point to be a documentation change (to inform users that the order is in fact unspecified), but the bug is still open, so I assume that it has not yet been fixed in the documentation.

Lauren answered 1/3, 2012 at 8:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.