w3c / uievents

UI Events
https://w3c.github.io/uievents/
Other
145 stars 51 forks source link

Different click event order behavior between Firefox , ie , edge and Chrome/Safari #303

Open TurnerXi opened 3 years ago

TurnerXi commented 3 years ago

Hi,I found the different trigger order in different browser when I click the target element which bounded with useCapture=false first and then bounded with useCapture=true .

Steps to reproduce

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #a {            width: 100%;            height: 300px;            background-color: lightblue;        }
    </style>
</head>
<body>
    <div id="a"></div>
</body>
<script>
    var a = document.getElementById("a");
    a.addEventListener('click', function() {  console.log('a');  },false);
    a.addEventListener('click', function() {  console.log('a1');  }, true);
</script>
</html>

Results Chrome / Safari:

a1
a

Firefox / IE / Edge:

a
a1

according to the spec https://w3c.github.io/uievents/#event-flow

The capture phase: The event object propagates through the target’s ancestors from the Window to the target’s parent. This phase is also known as the capturing phase.

The target phase: The event object arrives at the event object’s event target. This phase is also known as the at-target phase. If the event type indicates that the event doesn’t bubble, then the event object will halt after completion of this phase.

The bubble phase: The event object propagates through the target’s ancestors in reverse order, starting with the target’s parent and ending with the Window. This phase is also known as the bubbling phase.

When I click the target element , it should be at-target phrase and trigger by listener order, but Chrome / Safari doesn’t . So, what`s going on ?

masayuki-nakano commented 3 years ago

ccing @smaug----

mustaqahmed commented 3 years ago

The details of event dispatch as defined in DOM doesn't seem to provide a specific order in which multiple event listeners should be called, right? In particular, the inner invoke step goes through the event listeners at a particular target using "for-each", not sure if that defines an order.

UberKluger commented 2 years ago

@mustaqahmed From For each

To iterate over a list, performing a set of steps on each item in order, use phrasing of the form "For each item of list", and then operate on item in the subsequent prose.

and from event handlers

The event listener registration happens only if the event handler's value is being set to non-null, and the event handler is not already activated. Since listeners are called in the order they were registered, assuming no deactivation occurred, the order of event listeners for a particular event type will always be:

  1. the event listeners registered with addEventListener() before the first time the event handler's value was set to non-null

  2. then the callback to which it is currently set, if any

  3. and finally the event listeners registered with addEventListener() after the first time the event handler's value was set to non-null.

Note: a list is an ordered collection of items as opposed to a set which is specifically not ordered or a generic collection which is or isn't ordered, depending upon the implementation.

@TurnerXi

When I click the target element , it should be at-target ph(r)ase and trigger by listener order, but Chrome / Safari doesn’t .

Actually, according to W3 standard, all events propagate from the top level (window/html element) to the target and then back out again. These are the (respectively) capture phase and bubble phase. The calls to addEventListener specify that listener a is not in the capture phase (capture == false) while a1 is in the capture phase (capture == true) thus a1 should execute before a irrespective of the "order" of the listeners (defined by their creation order). So, in fact, Chrome / Safari are "correct" while the others are not. Some investigation suggests that this is because these browsers do not recognise the distinction between capturing and bubbling, processing events using only one of the phases (browser dependent) and ignoring the capture flag. Thus, since both listeners are attached to the same element, the event reaches them at the same time and the listener list order then decides which executes first.