Without kj-trigger, elements use the natural event: submit for forms, change for inputs and selects, click for everything else. Name any DOM event to override it, and add delay:Nms to wait until the user pauses — one request per pause instead of one per keystroke.
Three special triggers handle timing rather than events: load (immediately), revealed (when scrolled into view) and every Ns (polling). They have their own pages: lazy loading and polling.
Try “admin”, “ab”, or your name.
All three call PHP: validate.php, search.php, tab.php. Zero lines of JavaScript.
View the source for this demo
<div class="grid g2">
<div class="field">
<label for="tr-user">Username <small class="muted">· keyup delay:400ms</small></label>
<input id="tr-user" name="username" type="text" autocomplete="off" spellcheck="false"
kj-get="/api/validate.php" kj-trigger="keyup delay:400ms" kj-target="#tr-user-msg"
aria-describedby="tr-user-msg">
<p id="tr-user-msg" aria-live="polite" style="margin:4px 0 0;font-size:14.5px">Try “admin”, “ab”, or your name.</p>
</div>
<div class="field">
<label for="tr-q">Package search <small class="muted">· keyup delay:250ms</small></label>
<input id="tr-q" name="q" type="search" autocomplete="off"
kj-get="/api/search.php" kj-trigger="keyup delay:250ms" kj-target="#tr-results">
<ul id="tr-results" class="list-plain" aria-live="polite" style="font-family:var(--mono);font-size:14px"></ul>
</div>
<div class="field">
<label for="tr-tab">Module <small class="muted">· default trigger: change</small></label>
<select id="tr-tab" name="tab" kj-get="/api/tab.php" kj-target="#tr-tab-out">
<option value="core">Core</option><option value="hyper">Hyper</option><option value="pulse">Pulse</option><option value="motion">Motion</option>
</select>
<div id="tr-tab-out" aria-live="polite" style="margin-top:8px"></div>
</div>
</div>More examples
Trigger cheatsheet
<input name="q" kj-get="/search" kj-trigger="keyup delay:300ms" kj-target="#hits">
<div kj-get="/preview" kj-trigger="mouseenter" kj-target="#card">Hover</div>
<form kj-post="/save" kj-trigger="change">…autosave on any field change…</form>
<div kj-get="/widgets/weather" kj-trigger="load"></div>
<div kj-get="/comments" kj-trigger="revealed"></div>
<span kj-get="/price" kj-trigger="every 5s"></span>API
When to fire: any DOM event (with optional debounce), load (immediately), revealed (when scrolled into view, via IntersectionObserver), or every Ns (polling).
Migrating
delay:, load, revealed, every Ns. Not included: multiple events in one trigger, changed, once, throttle:, from:, intersect options and [filter] expressions.Related features
Polling
Keep a panel fresh with kj-trigger="every Ns".
Open →HyperLazy loading & infinite scroll
Load content when it scrolls into view with kj-trigger="revealed", or immediately with kj-trigger="load".
Open →UtilitiesDebounce & throttle
See the difference between debounce and throttle in real time: raw, throttled and debounced event lanes driven by your pointer.
Open →