Documents v0.9.0 · previews run the real runtime · experimental features are marked exp

prevent() and stop()

actionsince 0.7.0experimentalrequires runtime

Event modifiers for :on(): prevent() cancels the browser's default for the event, stop() ends the event at the element so ancestors do not see it.

On this page · 10 sections+

Syntax

:on(<event>, prevent());:on(<event>, stop());:on(<event>, prevent(), stop());
:on()Rule · hover any red tokenAn event rule: when the event happens on the matched element (or inside it, for bubbling events), the rule’s state mutations run. The cascade picks one winning mutation per state, per trigger and per element.Reference →

registry syntax: prevent()

Parameters

None.

Effect

No value. prevent() calls event.preventDefault() synchronously while the event is dispatched. stop() calls event.stopPropagation() when the event reaches the element.

Example

tabs.cssreactive
.tabs {  --active: state(1);}.tabs [role=tablist]:on(keydown(ArrowRight), prevent()) {  --active: cycle(1, 2, 3);}
state()Function · hover any red tokenMakes each matching element the owner of an independent state with an initial value.Reference →

Correct and incorrect usage

correctHandle the arrow keys of a tab list, leave every other key alone

tabs.cssreactive
.tabs { --active: state(1); }.tabs [role=tablist]:on(keydown(ArrowRight), prevent()) {  --active: cycle(1, 2, 3);}.tabs [role=tablist]:on(keydown(ArrowLeft), prevent()) {  --active: cycle(3, 2, 1);}

Only ArrowRight and ArrowLeft are cancelled, so the page does not scroll while they move the selection. Tab, Space and PageDown keep their native behavior.

correctAsk before a link navigates

delete.cssreactive
.delete { --asking: state(false); }.delete a:on(click, prevent()) {  --asking: set(true);}

The link does not navigate; the state can show a confirmation. Without the script, the link works as a normal link.

correctKeep a click inside a card

card.cssreactive
.list { --selected: state(0); }.card { --open: state(false); }.list .card:on(click) { --selected: set(sibling-index()); }.card .more:on(click, stop()) { --open: toggle(); }

A click on .more opens the card without selecting it: stop() keeps the event from the ancestor's rule.

correctCancel only while a condition holds

reader.cssreactive
.reader { --locked: state(false); }.reader .page:on(keydown(PageDown)) {  --locked: toggle();}.reader:when(--locked) .page:on(keydown(PageDown), prevent()) {}

An element gets prevent() when any rule that applies to it for that event has it, and :when() is current for every event. PageDown scrolls while --locked is false and is cancelled while it is true. The rule with only a modifier is empty on purpose: keep it through minification.

incorrectA click rule on a container

card.cssreactive
.card { --open: state(false); }.card:on(click, prevent()) {  --open: toggle();}

This compiles, but it cancels the default of every click inside .card: links stop navigating, submit buttons stop submitting, checkboxes stop toggling and <summary> stops opening. Put the rule on the element whose default you mean to cancel.

incorrectprevent() on an event that cannot be cancelled

search.cssreactive
.search { --typing: state(false); }.search input:on(input, prevent()) {  --typing: set(true);}

A compile error: input events are not cancelable, so the rule is ignored.

incorrectA bare modifier

link.cssreactive
a.confirm:on(click, prevent) {  --asking: set(true);}

A compile error that asks for the function form, prevent().

Behavior

  • Modifiers, not actions. They follow the event in the selector, as functions: :on(click, prevent()), :on(keydown(Escape), prevent(), stop()). A bare prevent or stop is a compile error that asks for the function form. The rule's declarations hold only state mutations; a modifier-only rule such as a.confirm:on(click, prevent()) {} is allowed.
  • Why `prevent()` exists. A handled key, such as an arrow key in a tab list, would also scroll the page, and a click on a link would also navigate. prevent() cancels that native default for the event the rule handles, and only that one.
  • Synchronous. The browser decides the default while the event is dispatched, so a cancellation in the next frame would be too late. prevent() runs inside the library's delegated listener, during dispatch. That listener is non-passive only for event types that a live rule cancels; pages without prevent() keep passive listeners.
  • Any rule wins. The element gets prevent() if any rule that applies to it for that trigger has it, with conditions that are current for every event.
  • `stop()`. :on() rules on the element's ancestors do not run for this event; the element's own rules still run. The native propagation stops at the element too, with stopPropagation() (never stopImmediatePropagation()), so ancestors' listeners do not see the event. Listeners on the element and its descendants still receive it, and the capture phase is never stopped. outside() rules elsewhere are not affected.
  • Form controls keep their keys. A key pressed in an input, textarea, select or editable element is not cancelled by a rule on an ancestor. A rule on the control itself can cancel it.
  • Non-cancelable events. If event.cancelable is false (for example a script-created event without cancelable: true), nothing is cancelled and a warning is logged.

Supported contexts

  • prevent(): click, dblclick, contextmenu, mousedown, pointerdown, keydown, keyup, submit and drag (where it cancels the pointerdown). Anywhere else it is a compile error, and outside() never accepts it.
  • stop(): every :on() event except outside() and drag, where it is a compile error.
  • Trusted stylesheets only, like every :on() rule.

Limitations

  • Each modifier appears at most once, in either order, and takes no arguments: prevent(1) and a repeated stop() are compile errors.
  • There is no once modifier: guard the rule with a state, .b:when(not --done):on(click) { --done: set(true); }.
  • A click rule with prevent() on a container also cancels clicks on links, buttons and form controls inside it (see the incorrect example above).
  • Minifiers that remove empty rules (esbuild minify or minifySyntax, csso, Lightning CSS) delete modifier-only rules such as a:on(click, prevent()) {}. Use whitespace-only minification.
  • Firefox has no automated test run; see Browser compatibility for the manual, user-reported pass.

Examples

Experimental · syntax may change between minor versions