OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

How does setTimeout in a focusIn handler resolve before the click handler runs?

  • Thread starter Thread starter connexo
  • Start date Start date
C

connexo

Guest
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:


Code:
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.bind(this));
    }

    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`;
    }
  }
);

Code:
textarea {
  height: 12em;
  margin-top: 2em;
  width: 16em;
}

Code:
<a-b></a-b>
<br>
<textarea id="output"></textarea>

<p>I expect DOM event handling to be <strong>synchronous</strong>, regardless of the internal sequence of events in a browser.</p>
<p>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 <code>setTimeout</code> and in <code>requestAnimationFrame</code> run <em>before</em> the click handler runs.</p>
<p>Can someone explain this behavior?</p>
<p>See this MVCE:</p>
<p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-js lang-js prettyprint-override"><code>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.bind(this));
}

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`;
}
}
);</code></pre>
<pre class="snippet-code-css lang-css prettyprint-override"><code>textarea {
height: 12em;
margin-top: 2em;
width: 16em;
}
</code></pre>
<pre class="snippet-code-html lang-html prettyprint-override"><code><a-b></a-b>
<br>
<textarea id="output"></textarea></code></pre>
</div>
</div>
</p>
 
Top