Dom Event Precedence

DOM event precedence

This was not, so far as i know, explicitly defined in the past. Different browsers are free to implement event ordering however they see fit. While most are close enough for all practical purposes, there have been and continue to be some odd edge cases where browsers differ somewhat (and, of course, the many more cases where certain browsers fail to send certain events at all).

That said, the HTML 5 draft recommendation does make an attempt to specify how events will be queued and dispatched - the event loop:

To coordinate events, user
interaction, scripts, rendering,
networking, and so forth, user agents
must use event loops as described in
this section.

There must be at least one event loop
per user agent, and at most one event
loop per unit of related
similar-origin browsing contexts.

An event loop has one or more task
queues. A task queue is an ordered
list of tasks [...]
When a user agent is to queue a task,
it must add the given task to one of
the task queues of the relevant event
loop. All the tasks from one
particular task source must always be
added to the same task queue, but
tasks from different task sources may
be placed in different task queues.
[...]

[...]a user agent could have one task queue
for mouse and key events (the user
interaction task source), and another
for everything else. The user agent
could then give keyboard and mouse
events preference over other tasks
three quarters of the time, keeping
the interface responsive but not
starving other task queues, and never
processing events from any one task
source out of order. [...]

Note that last bit: it is up to the browser implementation to determine which events will be grouped together and processed in order, as well as the priority given to any particular type of event. Therefore, there's little reason to expect all browsers to dispatch all events in a fixed order, now or in the future.

Are event handlers in JavaScript called in order?

This has been changed with DOM3! While the DOM level 2 events specification did state

When the event reaches the target, any event listeners registered on the
EventTarget
are triggered. 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 current DOM level 3 events specification does now state

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. HTML5
defines the ordering of listeners registered through event handler
attributes.
[…]

Finally, the implementation MUST process all candidate event
handlers
in order […]

However, I can't find a reference to this behaviour in the DOM 4 draft any more.

How to change event execution order?

Please review this one:

<!DOCTYPE html><html>
<head> <meta charset="utf-8"> <title>Close window</title></head>
<body> <!-- here is your first AddEventlistener--> <button id="abc" onclick="temp1()">button</button>
<script type="text/javascript"> function temp1() { alert("temp1"); };
function temp2() { alert("temp2"); }
/*then here is your second AddEventlistener*/ var d = document.getElementById("abc"); d.addEventListener("click", temp2, false);
/*javascript will execute it in order if you want to change the order. remove first EventListener then register new one after. something like this: */ //remove listener d.onclick = null; //register new d.addEventListener('click', temp1); </script></body>
</html>

Order of execution of functions bound to an event in Javascript

Event handlers are always called in the order in which they were registered.

Once registered, you cannot insert additional handlers ahead of them[*].


[*] unless you are some how able to obtain a list of all the handlers, and their EventListener objects, and call removeEventListener to remove them, insert your own, and then reinsert the originals. In practise this is likely to be impossible.

DOM event precedence

This was not, so far as i know, explicitly defined in the past. Different browsers are free to implement event ordering however they see fit. While most are close enough for all practical purposes, there have been and continue to be some odd edge cases where browsers differ somewhat (and, of course, the many more cases where certain browsers fail to send certain events at all).

That said, the HTML 5 draft recommendation does make an attempt to specify how events will be queued and dispatched - the event loop:

To coordinate events, user
interaction, scripts, rendering,
networking, and so forth, user agents
must use event loops as described in
this section.

There must be at least one event loop
per user agent, and at most one event
loop per unit of related
similar-origin browsing contexts.

An event loop has one or more task
queues. A task queue is an ordered
list of tasks [...]
When a user agent is to queue a task,
it must add the given task to one of
the task queues of the relevant event
loop. All the tasks from one
particular task source must always be
added to the same task queue, but
tasks from different task sources may
be placed in different task queues.
[...]

[...]a user agent could have one task queue
for mouse and key events (the user
interaction task source), and another
for everything else. The user agent
could then give keyboard and mouse
events preference over other tasks
three quarters of the time, keeping
the interface responsive but not
starving other task queues, and never
processing events from any one task
source out of order. [...]

Note that last bit: it is up to the browser implementation to determine which events will be grouped together and processed in order, as well as the priority given to any particular type of event. Therefore, there's little reason to expect all browsers to dispatch all events in a fixed order, now or in the future.

Event Handling order

Events bubble "up" the DOM tree, so if you've got handlers for an element and its parent, the child element handler will be called first.

If you register more than one handler for an event on a single DOM element (like, more than one "click" handler for a single button), then the handlers are called in the order that they were attached to the element.

Your handlers can do a few things to change what happens after they're done:

  • With the passed-in event parameter, call event.preventDefault() to keep any "native" action from happening
  • call event.stopPropagation() to keep the event from bubbling up the DOM tree
  • return false from the handler, to both stop propagation and prevent default

Note that for some input elements (checkboxes, radio buttons), the handling is a little weird. When your handler is called, the browser will have already set the checkbox "checked" value to the opposite of its former value. That is, if you have a checkbox that is not checked, then a "click" handler will notice that the "checked" attribute will be set to "true" when it is called (after the user clicks). However, if the handler returns false, the checkbox value will actually NOT be changed by the click, and it will remain un-checked. So it's like the browser does half of the "native" action (toggling the element "checked" attribute) before calling the handler, but then only really updates the element if the handler does not return false (or call "preventDefault()" on the event object).

Execution order between React's useEffect and DOM event handler

I discussed the problem with my colleague and finally found a solution. He pointed out that new React Fiber engine should not ensure the order of execution of side-effects, and suggested adding a revision number to the state.

Here is an example. The incremented revision will always invoke useEffect even if the set value is not changed. Subscribers obtain state.value from Provider and don't need to concern about the underlying revision.

import React, { useCallback, useEffect, useReducer, useRef } from 'react';

const storageKey = 'name';
const defaultValue = 'John Doe';

const orDefault(value) = (value) =>
(typeof value !== 'undefined' && value !== null) ? value : defaultValue;

const initializer = (arg) => ({
value: orDefault(localStorage.getItem(storageKey)),
revision: 0,
});

const reducer = (state, newValue) => ({
value: newValue,
revision: state.revision + 1,
});

const useCachedValue = () => {
const [state, setState] = useReducer(reducer, null, initializer);
const initialized = useRef(false);

// save the updated value as a side-effect
useEffect(() => {
if (initialized.current) {
localStorage.setItem(storageKey, state.value);
} else {
// skip saving just after the (re-)initialization
initialized.current = true;
}
}, [state]);

// re-initialize when the storage has been modified by another window
const handleStorageEvent = useCallback((e) => {
if (e.key === null || e.key === storageKey) {
initialized.current = false;
setState(orDefault(e.newValue));
}
}, []);

useEffect(() => {
if (typeof window !== 'undefined') {
window.addEventListener('storage', handleStorageEvent);
return () => {
window.removeEventListener('storage', handleStorageEvent);
};
}
}, []);

return [state.value, setState];
};

const Context = React.createContext();

const Provider = (props) => {
const cachedValue = useCachedValue();
return (
<Context.Provider value={cachedValue}>
{props.children}
</Context.Provider>
);
};

The Order of Multiple Event Listeners

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. 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 DOM3 event spec, still a work in progress, introduces 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.



Related Topics



Leave a reply



Submit