demo · v150

Workaround Killer

Before v150 the canonical fix for "click handler sees stale selection" was setTimeout(..., 0). This page runs both shapes — plain handler vs. setTimeout-wrapped handler — against the same drag-selection. In Chrome 150+ the plain handler is correct on its own; the setTimeout is dead code.

Drag-select inside the textarea, then release the mouse button. Both panels log what their handler saw at the moment of click.

legacy — setTimeout(..., 0) workaround

waiting for click…

modern — plain click handler (v150+)

waiting for click…
// Pre-v150 idiom — needed because click ran before mouseup committed selection
input.addEventListener('click', () => {
  setTimeout(() => {
    const a = input.selectionStart, b = input.selectionEnd;
    handle(a, b);
  }, 0);
});

// v150+ — the workaround is now redundant
input.addEventListener('click', () => {
  const a = input.selectionStart, b = input.selectionEnd;
  handle(a, b);
});

why the workaround can go

The selection-update default action of mouseup now runs before the synthesised click dispatches. Click handlers see the committed selection range immediately — no deferred read needed. Codebases that carried the setTimeout(..., 0) dance for over a decade can rip it out; the only downside of leaving it in is that it pushes the work to a separate task, which is a minor latency cost.

see also

implementation reference

Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗