noLateEventControl (JavaScript)
Summary
Section titled “Summary”- Rule available since:
v2.6.5 - Diagnostic Category:
lint/nursery/noLateEventControl - This rule doesn’t have a fix.
- The default severity of this rule is warning.
- Sources:
- Same as
unicorn/no-late-event-control
- Same as
How to configure
Section titled “How to configure”{ "linter": { "rules": { "nursery": { "noLateEventControl": "error" } } }}Description
Section titled “Description”Disallow calling preventDefault(), stopPropagation(), or stopImmediatePropagation() after the browser has finished handling the event.
When an event happens, the browser runs each event handler for it, one after another.
preventDefault(), stopPropagation(), and stopImmediatePropagation() only work while
the browser is still running these handlers. When a handler pauses, the browser moves on
without waiting for it, so calling these methods afterwards does nothing: the browser still
performs the default action, such as following a link, and other handlers still receive
the event.
Code in a handler runs too late in these cases:
- The code comes after an
awaitor afor awaitloop in anasyncfunction. - The code is in a function nested inside the handler, such as a
setTimeout()or.then()callback. The nested function can run after the handler has finished. - The handler is a generator function (
function*). Calling a generator function doesn’t run its body.
Call these methods directly in the handler, before the first await.
The rule checks calls on a function parameter whose name looks like an event, such as e,
event, evt, or a camelCase name ending with Event like clickEvent. In TypeScript,
it also checks parameters with an event type, such as MouseEvent or React’s
SyntheticEvent, and ignores parameters with a type that isn’t an event, such as string.
The rule has these limitations:
- Every call inside a nested function is reported, even when the nested function runs right away, like the callback of
array.map(). Move the call to the handler itself to make it clear that it runs in time. - The rule doesn’t follow the order in which code runs. A call is reported when an
awaitappears earlier in the handler, even if theawaitis in a branch that never runs together with the call. - When the event is passed to another function, calls inside that function are not checked.
Examples
Section titled “Examples”Invalid
Section titled “Invalid”element.addEventListener("click", async (event) => { await save(); event.preventDefault();});code-block.js:3:5 lint/nursery/noLateEventControl ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ event.preventDefault() is called after the event handler pauses.
1 │ element.addEventListener("click", async (event) => {
2 │ await save();
> 3 │ event.preventDefault();
│ ^^^^^^^^^^^^^^^^^^^^^^
4 │ });
5 │
ℹ The event handler pauses here.
1 │ element.addEventListener("click", async (event) => {
> 2 │ await save();
│ ^^^^^^^^^^^^
3 │ event.preventDefault();
4 │ });
ℹ When the handler pauses, the browser finishes handling the event without waiting for it, so this call has no effect.
ℹ Call event.preventDefault() before the event handler pauses.
ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
element.addEventListener("click", (event) => { setTimeout(() => { event.stopPropagation(); });});code-block.js:3:9 lint/nursery/noLateEventControl ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ event.stopPropagation() is called inside a function nested in the event handler.
1 │ element.addEventListener("click", (event) => {
2 │ setTimeout(() => {
> 3 │ event.stopPropagation();
│ ^^^^^^^^^^^^^^^^^^^^^^^
4 │ });
5 │ });
ℹ The nested function may run after the browser finishes handling the event, when this call has no effect.
ℹ Call event.stopPropagation() directly in the event handler, before any delayed work.
ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
element.onclick = function* (event) { event.preventDefault();};code-block.js:2:5 lint/nursery/noLateEventControl ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ event.preventDefault() is called inside a generator function.
1 │ element.onclick = function* (event) {
> 2 │ event.preventDefault();
│ ^^^^^^^^^^^^^^^^^^^^^^
3 │ };
4 │
ℹ Calling a generator function doesn't run its body, so this call doesn't happen while the browser handles the event.
ℹ Use a regular function as the event handler.
ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.
element.addEventListener("click", async (event) => { event.preventDefault(); await save();});
element.addEventListener("click", (event) => { event.stopPropagation(); setTimeout(() => { update(); });});Related links
Section titled “Related links”Copyright (c) 2023-present Biome Developers and Contributors.