I expect DOM event handling to be synchronous, regardless of the internal sequence of events in a browser.
For reasons I don't understand the click handler is massively delayed (varying between 30-90ms on my machine, Macbook Pro M1 Max, Chrome 126.0.6478.63), so both the code in setTimeout
and in requestAnimationFrame
run before the click handler runs.
Can someone explain this behavior?
See this MVCE:
const css = `:host(:focus-within) div { outline: 1px solid blue; display: inline-block; }`;
const sheet = new CSSStyleSheet();
sheet.replaceSync(css);
customElements.define(
"a-b",
class extends HTMLElement {
input = document.createElement("input");
btn = Object.assign(document.createElement("button"), {
textContent: "Click me"
});
constructor() {
super().attachShadow({ mode: "open" }).adoptedStyleSheets.push(sheet);
this.shadowRoot.append(this.input, this.btn);
this.addEventListener("focusin", this.handleFocusIn);
this.addEventListener("click", this.handleClick);
}
handleFocusIn() {
output.value += `focusin: ${Date.now()}\n`;
setTimeout(() => {
// How is this executed before the click listener runs??
output.value += `timeout: ${Date.now()}\n`;
}, 0);
requestAnimationFrame(() => {
// How is this executed before the click listener runs??
output.value += `rAF: ${Date.now()}\n`;
});
}
handleClick() {
output.value += `click: ${Date.now()}\n----------------------\n`;
}
}
);
textarea {
height: 12em;
margin-top: 2em;
width: 16em;
}
<a-b></a-b>
<br>
<textarea id="output"></textarea>
mousedown
instead?click
event will not come beforemouseup
. So if you're only interested in themousedown
, then yes, use that event. Note that it is triggered beforefocusin
.