Event Modifiers

Event modifiers give you fine-grained control over how events are handled. Chain multiple modifiers together for precise event behavior.

Overview

Modifiers are applied after the event name using dot notation. Multiple modifiers can be chained together:

html

                 
<button on-click.prevent.stop.self="handleClick()">Click Me</button>
<input on-keydown.ctrl.s.exact.debounce.500="save()"> 

        

Action Modifiers

Control the default behavior and propagation of events.

.prevent

Calls e.preventDefault() to prevent default browser behavior.

html

                 
<!-- Prevents form submission -->
<form on-submit.prevent="handleSubmit()">
    <input type="text">
    <button type="submit">Submit</button>
</form>

<!-- Prevents link navigation -->
<a href="/page" on-click.prevent="navigate()">Click Me</a> 

        

.stop

Calls e.stopPropagation() to prevent event bubbling.

html

                 
<div on-click="console.log('Parent')">
    <button on-click.stop="console.log('Child')">
        Click Me
    </button>
    <!-- Only "Child" logs, parent doesn't fire -->
</div> 

        

.once

Removes the event listener after the first trigger.

html

                 
<button on-click.once="initialize()">
    Initialize (only works once)
</button> 

        

.passive

Adds a passive event listener for better scroll performance.

html

                 
<div on-scroll.passive="handleScroll()">
    <!-- Optimized for smooth scrolling -->
</div> 

        

.capture

Uses capture phase instead of bubbling phase.

html

                 
<div on-click.capture="handleBeforeChild()">
    <button on-click="handleChild()">
        <!-- Parent fires before child -->
    </button>
</div> 

        

Target Modifiers

Control which element should receive the event.

.self

Only triggers if the event originated from the element itself, not a child.

html

                 
<div on-click.self="handleDivClick()">
    <button>Click me</button>
    <!-- Clicking button doesn't trigger handleDivClick -->
</div> 

        

.not-self

Only triggers if the event originated from a child element.

html

                 
<div on-click.not-self="handleChildClick()">
    <button>Click me</button>
    <!-- Clicking button triggers handleChildClick -->
</div> 

        

.window

Attaches the event listener to the window object.

html

                 
<!-- Keyboard shortcuts anywhere on the page -->
<div on-keydown.window.ctrl.s.exact="saveDocument()">
    <!-- Ctrl+S works even when focused elsewhere -->
</div>

<!-- Resize handler -->
<div on-resize.window="handleResize()">
    <!-- Responds to window resize -->
</div> 

        

Mouse Button Modifiers

Restrict event triggers to specific mouse buttons.

.left

Only triggers on left mouse button click.

html

                 
<button on-click.left="handleLeftClick()">
    Left Click Only
</button> 

        

.middle

Only triggers on middle mouse button click.

html

                 
<button on-click.middle="handleMiddleClick()">
    Middle Click Only
</button> 

        

.right

Only triggers on right mouse button click.

html

                 
<button on-click.right="handleRightClick()">
    Right Click Only
</button> 

        
html

                 
<!-- Different actions for different buttons -->
<button on-click.left="select()" on-click.right.prevent="showContextMenu()" on-click.middle="openInNewTab()">
    Click me with any button!
</button> 

        

Keyboard Modifiers

Create keyboard shortcuts with system key combinations.

.ctrl

Requires the Ctrl key to be pressed.

html

                 
<input on-keydown.ctrl.s="save()">
<!-- Ctrl+S saves -->

<button on-click.ctrl="save()">
    Ctrl + Click to Save
</button> 

        

.shift

Requires the Shift key to be pressed.

html

                 
<input on-keydown.shift.enter="sendMessage()">
<!-- Shift+Enter sends message --> 

        

.alt

Requires the Alt key to be pressed.

html

                 
<input on-keydown.alt.x="extraAction()">
<!-- Alt+X triggers extra action --> 

        

.meta

Requires the Meta key (Cmd on Mac, Windows key on PC).

html

                 
<input on-keydown.meta.s="save()">
<!-- Cmd+S on Mac, Win+S on PC --> 

        

.exact

Ensures only the specified modifiers are pressed. No extra modifiers allowed.

html

                 
<!-- Triggers only on Ctrl+S, not Ctrl+Shift+S -->
<input on-keydown.ctrl.s.exact="save()">

<!-- Triggers only on Ctrl, no other keys -->
<div on-click.ctrl.exact="handleCtrlClick()">
    Click with Ctrl (no Shift, Alt, or Meta)
</div> 

        

Key Aliases

Use friendly names for common keys instead of remembering key codes.

.enter Enter/Return key
.tab Tab key
.delete Delete or Backspace
.esc Escape key
.space Spacebar
.up Arrow Up
.down Arrow Down
.left Arrow Left
.right Arrow Right
.home Home key
.end End key
.pageup Page Up
.pagedown Page Down
.a through .z Letter keys
html

                 
<!-- Using key aliases -->
<input on-keydown.enter="submitForm()">
<input on-keydown.esc="closeModal()">
<input on-keydown.up="previousItem()">
<input on-keydown.down="nextItem()">

<!-- Key aliases with system modifiers -->
<input on-keydown.ctrl.enter="submitAndStay()">
<input on-keydown.shift.delete="permanentDelete()"> 

        

Wheel Direction Modifiers

Handle scroll wheel events based on direction.

.wheel-up

Triggers when scrolling up.

html

                 
<div on-wheel.wheel-up="previousPage()">
    Scroll up to go back
</div> 

        

.wheel-down

Triggers when scrolling down.

html

                 
<div on-wheel.wheel-down="nextPage()">
    Scroll down to go forward
</div> 

        

.wheel-left

Triggers when scrolling left (horizontal scroll).

html

                 
<div on-wheel.wheel-left="previousImage()">
    Scroll left for previous
</div> 

        

.wheel-right

Triggers when scrolling right (horizontal scroll).

html

                 
<div on-wheel.wheel-right="nextImage()">
    Scroll right for next
</div> 

        

Form State Modifiers

Trigger events based on form input states.

.dirty

Triggers only if the input has been modified.

html

                 
<input on-change.dirty="markAsDirty()"> 

        

.pristine

Triggers only if the input hasn't been modified.

html

                 
<input on-change.pristine="useDefaultValue()"> 

        

.valid

Triggers only if the input passes validation.

html

                 
<input type="email" required on-change.valid="saveEmail()"> 

        

.invalid

Triggers only if the input fails validation.

html

                 
<input type="email" required on-change.invalid="showError()"> 

        
html

                 
<!-- Complete form validation example -->
<form on-submit.prevent="handleSubmit()">
    <div>
        <label>Email</label>
        <input type="email" required @value="@{email.value}" on-change.valid="emailValid.value = true" on-change.invalid="emailValid.value = false">
        <div if="!emailValid.value && email.value">
            <d-text class="text-red-500 text-sm">Please enter a valid email</d-text>
        </div>
    </div>
    
    <div>
        <label>Username</label>
        <input type="text" minlength="3" @value="@{username.value}" on-input.dirty="checkUsername()">
    </div>
    
    <button type="submit" if="emailValid.value && username.value.length >= 3">
        Submit
    </button>
</form> 

        

Timing Modifiers

Control when and how often events execute with debouncing and throttling.

.debounce

Delays execution until after the last event. Great for search inputs.

html

                 
<!-- Debounce search with custom delay (500ms default) -->
<input on-input.debounce.300="search()">

<!-- Debounce with different delay -->
<input on-input.debounce.1000="expensiveSearch()"> 

        
💡 The number after the dot (e.g., .300) sets the delay in milliseconds. Default is 300ms.

.throttle

Limits execution to at most once per interval. Perfect for scroll events.

html

                 
<!-- Throttle scroll with custom interval (500ms default) -->
<div on-scroll.throttle.200="handleScroll()">

<!-- Throttle with different interval -->
<div on-scroll.throttle.1000="updatePosition()">
                        </div></div> 

        
💡 The number after the dot (e.g., .200) sets the throttle interval in milliseconds. Default is 300ms.
html

                 
<!-- Practical examples -->
<div class="space-y-4">
    <!-- Real-time search with debounce -->
    <div>
        <label>Search</label>
        <input type="text" @value="@{query.value}" on-input.debounce.300="performSearch()" placeholder="Type to search...">
        <div if="searching.value">Searching...</div>
        <ul for-each="result in results.value" for-key="{{results.id}}">
            <li>@{result}</li>
        </ul>
    </div>
    
    <!-- Scroll position tracking with throttle -->
    <div on-scroll.throttle.100="updateScrollPosition()" style="max-height: 300px; overflow-y: auto;">
        <!-- Long content here -->
        <p>Scroll position: @{scrollY.value}px</p>
    </div>
    
    <!-- Window resize with throttle -->
    <div on-resize.window.throttle.500="updateLayout()">
        Current width: @{windowWidth.value}px
    </div>
</div> 

        

Advanced Combinations

Combine multiple modifiers for complex event handling scenarios.

html

                 
<!-- Rich text editor shortcuts -->
<div on-keydown.ctrl.b.exact="bold()" on-keydown.ctrl.i.exact="italic()" on-keydown.ctrl.u.exact="underline()" on-keydown.ctrl.s.exact="save()" contenteditable="true">
    <!-- Rich text editor content -->
</div>

<!-- Drag and drop with modifiers -->
<div on-mousedown.left="startDrag()" on-mousemove.window.throttle.16="updateDrag()" on-mouseup.window="endDrag()">
    Draggable element
</div>

<!-- Modal with multiple interaction methods -->
<div class="modal">
    <!-- Click outside to close -->
    <div out-click.self="closeModal()">
        <!-- ESC key to close -->
        <div on-keydown.esc="closeModal()">
            <!-- Click close button -->
            <button on-click.prevent="closeModal()">×</button>
            
            <!-- Prevent clicks inside from closing -->
            <div on-click.self="console.log('Modal content clicked')">
                Modal content
            </div>
        </div>
    </div>
</div>

<!-- Form with all validation modifiers -->
<form on-submit.prevent="handleSubmit()">
    <input type="email" required @value="@{email.value}" on-change.dirty.valid="emailValid = true" on-change.dirty.invalid="emailValid = false" on-change.pristine="emailValid = null">
    
    <input type="password" minlength="8" @value="@{password.value}" on-input.debounce.300.valid="checkPasswordStrength()">
    
    <button type="submit" if="emailValid && password.value.length >= 8">
        Register
    </button>
</form> 

        

Quick Reference

Category Modifier Description
Action .prevent Prevents default browser behavior
.stop Stops event propagation
.once Triggers only once
.passive Adds passive event listener
Target .self Only triggers on the element itself
.not-self Triggers only on children
.window Attaches to the window object
Mouse .left Left mouse button only
.middle Middle mouse button only
.right Right mouse button only
Keyboard .ctrl Requires Ctrl key
.shift Requires Shift key
.alt Requires Alt key
.meta Requires Meta key (Cmd/Win)
.exact Only specified modifiers allowed
Wheel .wheel-up Scrolling up
.wheel-down Scrolling down
.wheel-left Scrolling left
.wheel-right Scrolling right
Form .dirty Input has been modified
.pristine Input hasn't been modified
.valid Input passes validation
.invalid Input fails validation
Timing .debounce Delays execution after last event
.throttle Limits execution frequency

Best Practices

Use .exact for Keyboard Shortcuts

When defining keyboard shortcuts, always use .exact to prevent accidental triggers when extra modifiers are pressed.

Combine .debounce with .dirty

For search inputs, combine .debounce with .dirty to only search when the user has actually typed something.

Use .throttle for Performance

Always throttle scroll and resize events to prevent performance issues. A 100-200ms throttle is usually sufficient.

Combine .self with out-*

For click-outside behavior, combine .self with out-click to ensure it only triggers when clicking outside the element.

Use .passive for Smooth Scrolling

Use .passive with scroll events to improve scrolling performance, especially on mobile devices.