event handler
FeatureEvent handlers are used to handle events with hyperscript. They are typically embedded directly on the element that is responding to the event.
on [every] <event-name>[(<param-list>)][\[<filter>\]] [<count>] [from <expr>] [<debounce> | <throttle>]
{ or [every] <event-name>[(<param-list>)][\[<filter>\]] [<count>] [from <expr>] [<debounce> | <throttle>] }
[queue (all | first | last | none)]
{<command>}
[end]
If the every
prefix is used, the event handler will not be synchronized (see queueing below.)
The event-name
can be a symbol, a dot-separated symbol or a string that names the event. The most obvious events
of interest are are standard HTML DOM events such as click,
focus/blur, change, etc. There are many, many standard browser events that
may be of interest depending on what you are trying to build. You may also wish to define your own events for higher level abstractions.
The optional param-list
is a comma separated list of parameter names. Parameters will be set from properties directly
on the event or in the details
property.
The optional filter
is a boolean expression that will filter the event. Symbols in the expression will be resolved
against the event first, then against the global scope.
The optional count
is a count filter with a value of either a specific number, a range or an unbounded start:
on click 1
on click 2 to 10
on click 11 and on
You can then optionally listen to an event from another element using the from <expr>
syntax, including the special
value elsewhere
, which will listen for the event from elsewhere in the DOM. (This is useful if you want "click-away to
close" behavior.)
Finally an event can specify a debounced at
or throttled at
value to debounce or throttle the events.
-- will wait until 500ms have passed without a keyup to trigger
on keyup debounced at 500ms ...
-- will fire every 500ms regardless of the number of events
on mousemove throttled at 500ms ...
Events can be repeated separated by an or
to assign one handler to multiple events:
<div _="on click or touchstart fetch /example then put it into my innerHTML">
Fetch it...
</div>
The queue
keyword allows you to specify an event queue strategy across all events for the handler (see queueing below.)
The body is a list of commands, optionally separated by the then
keyword
The end
is optional if you are chaining on
features together
The on
feature is the primary way to hook hyperscript into the DOM event system. It is typically placed on
DOM elements directly, using the _
, script
or data-script
attribute.
The on
handler can specify parameters. The value of these parameters destructured from properties on the event
orevent.detail
object of the triggering event and matched by name.
So if an event has the value event.detail.foo = "bar"
then the on
declaration could look like this:
<div _="on anEvent(foo) log foo">Log Foo</div>
The event
symbol is always available in an on
feature and is set to the triggering event. So the above could
be written in the following more long-winded manner:
<div _="on anEvent log event.detail.foo">Log Foo</div>
When the element is removed, the listener is removed too -- even if it's listening to another element that's still in the document:
Count: <output _="
on click from #inc
log "Increment"
increment my textContent
init
remove me
">0</output>
<!--After the <output/> is removed, clicking this will not log anything to
the console-->
<button id="inc">Increment</button>
You can control the event queuing behavior of an event handler by using the every
and queue
keyword.
If you prefix the event with every
then every time the event is triggered the event handler will fire, even
if a previous event has not completed. The event handlers will run in parallel, and there will be no
queuing of events.
If you postfix the event with queue
you may pick from one of four strategies:
none
- Any events that arrive while the event handler is active will be droppedall
- All events that arrive will be added to a queue and handled in orderfirst
- The first event that arrives will be queued, all others will be droppedlast
- The last event that arrives will be queued, all others will be droppedqueue last
is the default behavior
If an exception occurs during an event handler, the exception
event will be triggered on the element, and may
be handled as a normal event:
<div
_="on click call mightThrow()
on exception(error) log error"
>
Click Me!
</div>
Hyperscript includes a few synthetic events that make use of more complex APIs. For example, you can listen for
mutations on an element with the on mutation
form. This will use the Mutation Observer
API, but will act more like a regular event handler.
<div _='on mutation of @foo put "Mutated" into me'></div>
This div will listen for mutations of the foo
attribute on this div and, when one occurs, will put the value
"Mutated" into the element.
Another synthetic event is the intersection
event that uses the Intersection Observer
API. Again, hyperscript makes this API feel more event-driven:
<img
_="on intersection(intersecting) having threshold 0.5
if intersecting transition opacity to 1
else transition opacity to 0 "
src="https://placebear.com/200/300"
/>
This image will become visible when 50% or more of it has scrolled into view. Note that the intersecting
property
is destructured into a local symbol, and the having threshold
modifier is used to specify that 50% of the image
must be showing.
Here is a demo:
<div _="on click call alert('You clicked me!')">Click Me!</div>
<div
_="on mouseenter add .visible to #help end
on mouseleave remove .visible from #help end"
>
Mouse Over Me!
</div>
<div id="help">I'm a helpful message!</div>