Import functions:

import { closest, matches } from '/dom/module.js';

All functions that take more than one parameter are partially applicable.


closest(selector, node)

Returns the node itself or the closest ancestor that matches selector. If no match is found, returns undefined.


attribute(name, node)

Returns the string contents of attribute name. If the attribute is not set, returns undefined.


Returns the id of node, or where node has no id, a random id is generated, checked against the DOM for uniqueness, set on node and returned:

// Get ids of all buttons in document
select('button', document)
.forEach((id) => ...)

media(query, enterFn, exitFn)

Evaluates query object, which is an object describing a media and scroll query, against the document, and calls enterFn when all conditions in the selector object become true, and exitFn when at least one of them becomes false.

A query object may contain any combination of the properties:

    minWidth: number | string | fn,
    maxWidth: number | string | fn,
    minHeight: number | string | fn,
    maxHeight: number | string | fn,
    minScrollTop: number | string | fn,
    maxScrollTop: number | string | fn,
    minScrollBottom: number | string | fn,
    maxScrollBottom: number | string | fn

For each property a number represents a value in pixels, a string must be a value with CSS units (eg. '3rem'), or a function must return a number representing a value in pixels.


Returns the tag name of node, in lowercase.

const li = create('li', 'Salt and vinegar');
tag(li);   // 'li'


Returns one of 'element', 'text', 'comment', 'document', 'doctype' or 'fragment'.


assign(node, properties)

Assigns each property of properties to node, as a property where that property exists in node, otherwise as an attribute.

If properties has a property 'children' it must be an array of nodes; they are appended to ‘node’.

The property 'html' is treated as an alias of 'innerHTML'. The property 'tag' is treated as an alias of 'tagName' (which is ignored, as node.tagName is read-only). The property 'is' is also ignored.

create(tag, content)

Constructs and returns a new DOM node.

  • If tag is "text" a text node is created.
  • If tag is "fragment" a fragment is created.
  • If tag is "comment" a comment is created.
  • If tag is any other string the element <tag></tag> is created.
  • Where tag is an object, it must have a "tag" or "tagName" property. A node is created according to the above rules for tag strings, and other properties of the object are assigned with dom’s assign(node, object) function.

If content is a string it is set as text content on a text or comment node, or as inner HTML on an element or fragment. It may also be an object of properties which are assigned with dom’s assign(node, properties) function.


events(type, node)

Returns a mappable stream of events heard on node:

var stream = events('click', document.body);
.each(function(node) {
    // Do something with nodes

Stopping the stream removes the event listeners:



Returns true if user event is from the primary (normally the left or only) button of an input device. Use this to avoid listening to right-clicks.

gestures(options, node)

Returns a stream of streams of events. Each stream of events represents the motion of a single finger. The types of events the stream contains is either 'mousedown' followed by any number of 'mousemove's and a 'mouseup', or the touch objects that go with 'touchstart', any number of 'touchmove's and a 'touchend'.

gestures({ selector: '.thing', threshold: '0.5rem' }, document)
.each(function(events) {
    // First event is a mousedown or touchstart event
    const e0 = events.shift();

    events.each(function(e1) {
        // Mousemove or touchmove events
        const distance = Math.pow(
            Math.pow(e1.clientX - e0.clientX, 2),
            Math.pow(e1.clientY - e0.clientY, 2),


Constrains focus to focusable elements inside node. Returns a function that removes the trap. Calling trapFocus(node) again also removes the existing trap.


Returns key string corresponding to e.keyCode, or undefined.



Returns a prefixed CSS property name where a prefix is required in the current browser.


Returns a DOMRect object describing the draw rectangle of node. (If node is window a preudo-DOMRect object is returned).


Disables scrolling by setting overflow: hidden on node while maintaining the current scrollTop, effectively causing the node to ‘freeze’ in position.

style(property, node)

Returns the computed style property of node.

style('transform', node);            // returns transform

If property is of the form "property:name", a named aspect of the property is returned.

style('transform:rotate', node);     // returns rotation, as a number, in radians
style('transform:scale', node);      // returns scale, as a number
style('transform:translateX', node); // returns translation, as a number, in px
style('transform:translateY', node); // returns translation, as a number, in px


animate(duration, transform, name, object, value)

Animates property name of object to value over duration seconds, using the transform function as an easing function, and updates the object on animation frames.

duration  - number in seconds
transform - function that maps x (0-1) to y (0-1)
name      - string name of property to animate
object    - object to animate
value     - target value

transition(duration, fn)

Calls fn on each animation frame until duration seconds has elapsed. fn is passed a single argument progress, a number that ramps from 0 to 1 over the duration of the transition. Returns a function that cancels the transition.

transition(3, function(progress) {
    // Called every frame for 3 seconds



request(type, url, data, mimetype | headers)

Uses fetch() to send a request to url. Where type is "GET", data is serialised and appended to the URL, otherwise it is sent as a request body. The 4th parameter may be a content type string or a headers object (in which case it must have a 'Content-Type' property).


The library has a number of scripts for cheekily extending the default behaviour of DOM elements, declared via 'behaviour' attributes.

No style is given to make these behaviours do anything visual however. Behaviours, largely, add and remove classes, how that looks is up to your CSS. This is deliberate: by seperating style and behaviour many different styles of interactive — a menu, a dialog, a tooltip — may share, for example, popable behaviour.




An element with a locateable attribute updates the browser location hash with its id when scrolled into view.

When the location hash changes to be equal to a locateable‘s id the locateable gets the class "located", and links that reference that locateable via their href attribute get the class "on".

Build a list of links that reference locateables and with a little style you have a scrolling navigation:

    a               { color: #888888; }
    a.on            { color: black; }
    article.located { ... }

<a href="#fish">...</a>
<a href="#chips">...</a>

<article locateable id="fish">...</article>
<article locateable id="chips">...</article>



Links refering to [fullscreenable] elements put those elements into fullscreen mode when clicked.

Fullscreen capability is not reliably queried in CSS (through @supports or other means), so this script also adds the class fullscreen-support to the document root in navigators where support is detected, for styling of UI that depends on fullscreen support.


A switchable is given the class "active" when a link that references it is clicked, and all links to it are given the class "on". In any group of siblings with the switchable attribute, exactly one is always active.

Switchables can be used to make tabs, slideshows, accordions and so on.

    <a class="tab-button button on" href="#tab-1">1</a>
    <a class="tab-button button" href="#tab-2">2</a>
    <a class="tab-button button" href="#tab-3">3</a>

<section class="tab-block block active" switchable id="tab-1">
    Tab 1

<section class="tab-block block" switchable id="tab-2">
    Tab 2

<section class="tab-block block" switchable id="tab-3">
    Tab 3


An element with the toggleable attribute is activated and deactivated when a link that references it is clicked.

An active toggleable has the class "active", and links to it have the class "on".

With a little hide/show style, a toggleable can be used to make menus, drawers, accordions and so on.




The validateable attribute can be set on an individual input, or on a form to make all descendant inputs validateable.

A validateable input is validated on focusout, and if an invalid event is emitted an error label is appended to the DOM directly after it. The label's text is read from…

  1. a data-validation-xxx attribute on the input, (where xxxx is the name of the failed constraint), OR
  2. the object dom.validation.messages, OR
  3. the browser's default message

In addition, the first time a validation is performed on an element the class "validated" is added, providing a hook for pre- and post- validation :invalid styles.

Constraints are named after the validation attributes that impose them.

dom.validation.messages = {
    pattern:   'Pattern does not match',
    max:       'Number too small',
    min:       'Number too big',
    step:      'Number not in step',
    maxlength: 'Input too long',
    type:      'Input not of type',
    required:  ''

This messages object is unpopulated by default, so the browser's default validation messages are shown.