The primary use case for hyperscript is adding small bits of interactivity to the DOM and, as such, it has a lot of syntax for making this easy and natural.
We have glossed over a lot of this syntax in previous examples (we hope it was intuitive enough!) but now we will get into the details of what they all do:
There are two sides to DOM manipulation: finding stuff and mutating it. In this section we will focus on how to find things in the DOM.
You are probably used to things like number literals (e.g. 1) or string literals (e.g. "hello world").
Since hyperscript is designed for DOM manipulation, it supports special literals that make it easy to work with the DOM.
Some are inspired by CSS, while others are our own creation.
Here is a table of the DOM literals:
.class name
.{expression}
A class literal starts with a . and returns all elements with that class.
#ID
#{expression}
An ID literal starts with a # and returns the element with that id.
<css selector />
A query literal is contained within a < and />, returns all elements matching the CSS selector.
@attribute name
An attribute literal starts with an @ (hence, attribute, get it?) and returns the value of that
attribute.
*style property
A style literal starts with an * (a reference to CSS Tricks) and returns the
value of that style property.
1em
0%
expression px
A measurement literal is an expression followed by a CSS unit, and it appends the unit as a string. So, the
above expressions are the same as "1em", "0%" and `${expression}px`.
Here are a few examples of these literals in action:
-- adds the 'disabled' class to the element with the id 'myDiv'
add .disabled to #myDiv
-- adds the 'highlight' class to all divs with the class 'tabs' on them
add .highlight to <div.tabs/>
-- sets the width of the current element to 35 pixels
set my *width to 35px
-- adds the `disabled` attribute to the current element
add @disabled to me
Class literals, ID Literals and Query Literals all support a templating syntax.
This allows you to look up elements based on a variable rather than a fixed value:
-- adds the 'disabled' class to the element with the id 'myDiv'
set idToDisable to 'myDiv'
add .disabled to #{idToDisable}
-- adds the 'highlight' class to all elements with the 'tabs' class
set classToHighlight to 'tabs'
add .highlight to .{classToHighlight}
-- removes all divs w/ class .hidden on them from the DOM
set elementType to 'div'
remove <${elementType}.hidden/>
All these language constructs make it very easy to work with the DOM in a concise, enjoyable manner.
Compare the following JavaScript:
document.querySelector('#example-btn')
.addEventListener('click', e => {
document.querySelectorAll(".elements-to-remove").forEach(value => value.remove());
})
with the corresponding hyperscript:
on click from #example-btn
remove .elements-to-remove
You can see how the support for CSS literals directly in hyperscript makes for a much cleaner script, allowing us to focus on the logic at hand.
Often you want to find things within a particular element. To do this you can use the in expression:
-- add the class 'highlight' to all paragraph tags in the current element
add .highlight to <p/> in me
Sometimes you wish to find the closest element in a parent hierarchy that matches some selector. In JavaScript
you might use the closest() function
To do this in hyperscript you can use the closest expression:
-- add the class 'highlight' to the closest table row to the current element
add .highlight to the closest <tr/>
Note that closest starts with the current element
and recurses up the DOM from there. If you wish to start at the parent instead, you can use this form:
-- add the class 'highlight' to the closest div to the current element, excluding the current element
add .highlight to the closest parent <div/>
You can use the positional expressions to get the first, last or a random element from a collection of things:
-- add the class 'highlight' to the first paragraph tag in the current element
add .highlight to the first <p/> in me
You can use the relative positional expressions next and previous to get an element
relative to either the current element, or to another element:
-- add the class 'highlight' to the next paragraph found in a forward scan of the DOM
add .highlight to the next <p/>
Note that next and previous support wrapping, if you want that.
Using the expressions above, you should be able to find the elements you want to update easily.
Now, on to updating them!
The most basic way to update contents in the DOM is using the set and put commands.
Recall that these commands can also be used to set local variables.
When it comes to updating DOM elements, the put command is much more flexible, as we will see.
First, let's just set the innerHTML of an element to a string:
<button _="on click set my innerHTML to 'Clicked!'">
Click Me
</button>
Using the put command would look like this:
<button _="on click put 'Clicked!' into my innerHTML">
Click Me
</button>
In fact, the put command is smart enough to default to innerHTML when you put something into an element, so we can
omit the innerHTML entirely:
<button _="on click put 'Clicked!' into me">
Click Me
</button>
The put command can also place content in different places based on how it is used:
<button _="on click put 'Clicked!' before me">
Click Me
</button>
The put command can be used in the following ways:
put content before element
Puts the content in front of the element, using Element.before.
put content at the start of element
Puts the content at the beginning of the element, using Element.prepend.
put content at the end of element
Puts the content at the end of the element, using Element.append.
put content after element
Puts the content after the element, using Element.after.
This flexibility is why we generally recommend the put command when updating content in the DOM.
One exception to this rule is when setting attributes, which we typically recommend using set.
It just reads better to us:
<button _="on click set @disabled to 'disabled'">
Disable Me
</button>
set is recommended for setting values into normal variables as well.
The default command sets a variable only if it is currently null, undefined, or empty:
default x to 10 -- only sets x if it has no value
default @count to 0 -- works with attributes too
A very common operation in front end scripting is adding or removing classes or attributes from DOM elements. hyperscript
supports the add, remove and toggle commands to help do this.
Here are some examples adding, removing and toggling classes:
<button _="on click add .red to me">
Click Me
</button>
<button class="red" _="on click remove .red from me">
Click Me
</button>
<button _="on click toggle .red on me">
Click Me
</button>
You can also add, remove and toggle attributes as well. Here is an example:
<button _="on click toggle @disabled on #say-hello">
Toggle Disabled State
</button>
<button id="say-hello" _="on click alert('hello!')">
Say Hello
</button>
Finally, you can toggle the visibility of elements by toggling a style literal:
<button _="on click toggle the *display of the next <p/>">
Toggle The Next Paragraph
</button>
<p>
Hyperscript is rad!
</p>
Hyperscript is rad!
The take command removes a class (or attribute) from a set of elements and adds it to a target,
making it perfect for "active item" patterns like tab bars and menus:
<ul _="on click from <li/>
take .selected from <li/> for the target">
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
This removes .selected from all <li> elements and adds it to the one that was clicked.
You can also take attributes with an optional replacement value:
take @aria-selected with "true" from <li/> for the target
You can also use the remove command to remove content from the DOM:
<button _="on click remove me">
Remove Me
</button>
The remove command is smart enough to figure out what you want to happen based on what you tell it to remove.
You can show and hide things with the show and hide commands:
<button _="on click
hide me
wait 2s
show me">
Peekaboo
</button>
By default, the show and hide commands will use the display style property. You can instead use visibility
or opacity with the following syntax:
<button _="on click
hide me with *opacity
wait 2s
show me with *opacity">
Peekaboo
</button>
The add, remove, show and hide commands all support a when clause to conditionally apply to each element.
After execution, the result contains the array of elements that matched the condition.
Here is an example using show ... when to filter a list:
<input _="on keyup show <li/> in #color-list
when its innerHTML contains my value">
<ul id="color-list">
<li>Red</li>
<li>Blue</li>
<li>Blueish Green</li>
<li>Green</li>
<li>Yellow</li>
</ul>
We mentioned this above, but as a reminder, you can toggle visibility using the toggle command:
<button _="on click toggle the *display of the next <p/>">
Toggle The Next Paragraph
</button>
<p>
Hyperscript is rad!
</p>
Hyperscript is rad!
You can transition a style from one state to another using the transition command. This
allows you to animate transitions between different states:
<button _="on click transition my *font-size to 30px
then wait 2s
then transition my *font-size to initial">
Transition My Font Size
</button>
The above example makes use of the special initial symbol, which you can use to refer to the initial value of an
elements style when the first transition begins.
The transition command is blocking: it will wait until the transition completes before the next command executes.
Another common way to trigger transitions is by adding or removing classes or setting styles directly on an element.
However, commands like add, set, etc. do not block on transitions.
If you wish to wait until a transition completes after adding a new class, you should use the settle command
which will let any transitions that are triggered by adding or removing a class finish before continuing.
<button style="transition: all 800ms ease-in"
_="on click add .red then settle then remove .red">
Flash Red
</button>
If the above code did not have the settle command, the button would not flash red because the class .red would be
added and then removed immediately
This would not allow the 800ms transition to .red to complete.
The start a view transition command wraps DOM mutations in a
View Transition,
animating between before and after snapshots of the DOM:
start a view transition
put newContent into #container
end
You can specify a transition type for CSS targeting:
start a view transition using "slide-left"
remove .active from .tab
add .active to me
put content into #panel
end
All animation timing and style is controlled via CSS (e.g. ::view-transition-old, ::view-transition-new).
If the browser does not support view transitions, the body runs normally with no animation.
See the start a view transition command for full details.
Sometimes you want to know the dimensions of an element in the DOM in order to perform some sort of translation or
transition. Hyperscript has a measure command that will give you measurement information
for an element:
<button _="on click measure my top then
put `My top is ${top}` into the next <output/>">
Click Me To Measure My Top
</button>
<output>--</output>
You can also use the pseudo-style literal form *computed-<style property> to get the computed (actual) style property
value for an element:
<button _="on click get my *computed-width
put `My width is ${the result}` into the next <output/>">
Click Me To Get My Computed Width
</button>
<output>--</output>
Hyperscript includes several additional commands for common DOM interactions:
The focus and blur commands set or remove keyboard focus:
focus #name-input
blur me
Both default to me if no target is given.
The empty command removes all children from an element:
empty #results
The select command selects the text content of an input or textarea:
select #search-input
The open and close commands work with dialogs, details elements and popovers:
open #my-dialog -- calls showModal() on a <dialog>
close #my-dialog -- calls close() on a <dialog>
open #my-details -- sets open attribute on a <details>
close #my-details -- removes open attribute from a <details>
For elements with a popover attribute, open and close call showPopover() and hidePopover() respectively.
As a fallback, they call .open() and .close() on the target.
You can also enter and exit fullscreen mode:
open fullscreen #video
close fullscreen
The ask and answer commands provide access to the browser's built-in dialogs:
ask "What is your name?"
put it into #greeting
answer "File saved!"
ask wraps prompt() and places the result in it. answer wraps alert() by default.
With two choices, answer wraps confirm() and the result is the chosen label:
answer "Save changes?" with "Yes" or "No"
if it is "Yes"
-- save...
end
As a nod to HyperTalk, hyperscript includes a speak command
that uses the Web Speech API for text-to-speech:
speak "Hello world"
speak "Hello" with voice "Samantha"
speak "Quickly now" with rate 2 with pitch 1.5
The command is async-transparent: it waits for the utterance to finish before continuing to the next command. You can
configure voice, rate, pitch, and volume using with clauses.