dom

Members

(static, constant) onDrag

Alias for drag

Deprecated:
  • Use drag instead
Source:
See:
  • drag

(static, constant) onSwipe

Alias for swipe

Deprecated:
  • Use swipe instead
Source:
See:
  • swipe

Methods

(static) addListenerForEvents(elements, events, handler, optionsopt)

Adds one listener to multiple events

Source:
Parameters:
Name Type Attributes Description
elements string | HTMLElement | NodeList

The elements or a selector for elements to add the event listeners to

events string | Array.<string>

The event types to add the event listeners for, like click mouseenter

handler function

The handler to call when the event is triggered.

options object <optional>

The options to pass to the event listeners

Example
addListenerForEvents('.foo', 'click mouseenter', (e) => { console.log(e.type) })

(static) css(element, styles, transform)

Sets element styles from passed object of styles. Can also transform dash-case to camelCase for CSS properties

Source:
Parameters:
Name Type Description
element HTMLElement

The element to set styles on

styles object

The object of styles to set

transform boolean

Whether to transform dash-case to camelCase for CSS properties

Example
css(document.getElementById('foo'), { 'background-color': 'red', 'font-size': '16px' }, true) // => sets background-color and font-size
css(document.getElementById('foo'), { backgroundColor: 'red', fontSize: '16px' }) // => sets background-color and font-size

(static) cssTimeToMilliseconds(duration) → {number}

Converts a duration string to milliseconds integer

Source:
Parameters:
Name Type Description
duration string

The duration string to convert, e.g. '1s', '100ms', '0.5s'

Returns:
Type:
number

The duration in milliseconds

Example
convertToMilliseconds('1s') // 1000
convertToMilliseconds('100ms') // 100
convertToMilliseconds('0.5s') // 500
convertToMilliseconds('0.5') // 0
convertToMilliseconds('foo') // 0

(static) decodeHTML(html) → {string}

Decodes HTML entities in a string using the browser's DOMParser. If the DOMParser is not available, it uses a regular expression to decode the basic entities.

Source:
See:
  • module:parsers.decodeHtmlEntities
Parameters:
Name Type Description
html string

The HTML string to decode

Returns:
Type:
string

The decoded HTML string

Example
decodeHTML('&lt;div&gt;foo&lt;/div&gt;') // => '<div>foo</div>'
decodeHTML('&lt;div&gt;foo&lt;/div&gt;&lt;div&gt;bar&lt;/div&gt;') // => '<div>foo</div><div>bar</div>'

(static) delegateEvent(selector, eventType, handler) → {MutationObserver|Object|null}

Delegate DOM events. Uses event bubbling with closest() for events that bubble. Uses MutationObserver for non-bubbling events (focus, blur, mouseenter, mouseleave, etc.) to attach listeners directly to matching elements as they appear in the DOM.

Source:
Parameters:
Name Type Description
selector string

The selector to select the elements to delegate the event to

eventType string

The event type to delegate, like click

handler function

The handler to call when the event is triggered.

Returns:
Type:
MutationObserver | Object | null

For non-bubbling events the MutationObserver instance (with an added destroy() method that also removes the element listeners), for bubbling events a handle with destroy()/disconnect(), or null when MutationObserver is required but unavailable

Example
delegateEvent('.foo', 'click', (e, target) => {
console.log('Clicked on', target)
})

(static) detachElement(element)

Detaches an element from the DOM and returns it

Source:
Parameters:
Name Type Description
element HTMLElement

The element to detach

Example
detachElement(element)
// => element
console.log(element.parentNode) // => null

(static) drag(target, opts) → {object|undefined}

Drag event handler

Pointer events, and one gesture at a time. The pointer is captured on the way in - touch and pen the browser captures implicitly, the mouse it does not - so a mouse drag keeps reporting after it has left the element, and whatever the pointer crosses on the way hears nothing of it. What capture does not survive is the element leaving the document: the moment it is disconnected the spec hands the capture to the document, and insertBefore on a connected node disconnects it first, so a list that reorders itself by moving the dragged row loses it on the first crossing. The moves are heard on the document for as long as the gesture lasts, which is where they land either way. Everything this needs - pointer events, pointer capture, touch-action - is in Safari 13 and iOS 13.2; below that the listener attaches and nothing ever arrives.

Two ways in. Hand it an element and it waits for a pointerdown of its own, for as long as it is attached. Hand it a pointerdown already in flight and it starts that one gesture now, taking its listeners away again when the pointer is let go - which is what a caller with a single delegated listener has, and the only shape that works over a list whose rows come and go. Attaching per row is a listener per row and a re-attach every time the list grows one; delegating is one listener whatever the list does.

Started from an event, nothing is written into the element: no drag-enabled, no dragging, no touch-action. It is an element the caller already owns and has already styled, and touch-action is decided long before a pointerdown is dispatched, so setting it there would be a promise this cannot keep - put it on the handle in CSS.

A gesture the platform takes away - a scroll it decided was one, a call arriving - reports dragcancel and no dragend, and never coasts into inertia. That distinction is the whole reason to listen for it: a cancelled drag is not a drag the person finished.

within is what makes the percentages worth reading. They are measured against the element being dragged unless it names another, and a handle is a few pixels of the track it runs along - so xPercentage off a 24px handle answers a question about the handle, and the caller wanting "how far along the track is this" has to do the sum itself. Pointed at the track it is that number directly, held to 0-100 at the ends, which is a slider, a splitter or a before-and-after comparison in one read. maxVelocity: '0.5%' caps the flick in that unit too.

axis is for a handle on a track. The glide is two-dimensional and over once both velocities have decayed, and a slider reads one of them: a flick along a horizontal track carries some velocity across it too, and that one can outlive the one along the track by most of a second - the handle sat still at the wall, draginertiaend not yet said. Named, the other axis reads 0 in every velocityX/velocityY and carries nothing, so the glide is over when this one is.

preventDefaultTouch owns the touch gesture by putting touch-action: none on the element for as long as the handler is attached, restored by destroy. Preventing the default on the events cannot do it: by the time a pointermove arrives the browser has already decided the gesture is a scroll.

The events are named dragstart, drag and dragend, which are also the names the native HTML drag and drop API uses. A page listening for the native ones on the same element will hear these too. Renaming them is a breaking change and has not been made; callback is the way to take one element's drags without that.

What shaped it. compare-images-slider is the handle on a track: within, axis, and a flick capped in per cent because a comparison slider is the same gesture at every width. splitter-elemental in book-of-elements is that read on either axis, and rearrange-elemental is the delegated way in - one pointerdown over rows that come and go, losing the pointer capture on the first insertBefore. Every option above is one of those three hitting a wall, not a guess at what a caller might want.

Source:
Parameters:
Name Type Description
target HTMLElement | PointerEvent

The element to listen for drag gestures on, or a pointerdown already in hand to start one gesture from now

opts object | function

The options object or the callback to call when a drag gesture is detected

Name Type Attributes Default Description
target HTMLElement <optional>

Where to capture the pointer and dispatch the events, when started from an event. Defaults to that event's currentTarget, then its target - name it when the listener is on a container and the gesture belongs to a handle inside it

within HTMLElement <optional>

The box relativeX/relativeY and xPercentage/yPercentage are measured against, and the one inertia bounces off. Defaults to the element being dragged; name the track when the thing being dragged is a handle running along one. Anything that is not an element falls back to that default

inertia boolean <optional>
false

Whether to enable inertia

bounce boolean <optional>
false

Whether to enable bounce when inertia is enabled

friction number <optional>
0.9

The friction to apply when inertia is enabled

bounceFactor number <optional>
0.2

The bounce factor to apply when bounce is enabled

velocityWindow number <optional>
80

Time window (ms) over which flick velocity is measured, ending at the release - a drag held still before letting go carries none

maxVelocity number | string <optional>
2

Cap on flick velocity magnitude, in pixels per millisecond - or, as a string ending in %, in per cent of within per millisecond, width for x and height for y: the unit the percentages are in, and the one that keeps the same flick reading the same on a narrow track and a wide one. Anything that does not parse as a number is the default

axis 'x' | 'y' <optional>

The axis the flick and the glide run along; the velocity across it reads 0 and carries nothing. Anything else is both axes

preventDefaultTouch boolean <optional>
true

Whether to take the touch gesture, with touch-action: none on the element. Ignored when started from an event - by then the browser has already decided whether the gesture is a scroll, so the handle's stylesheet is the only place it can be said

callback function <optional>

The callback to call when a drag gesture is detected

Returns:
Type:
object | undefined

The destroy method to remove the event listeners; nothing when refused - not an element, already attached, or already mid-gesture

Example
drag(document.getElementById('foo'), (e) => {
 console.log(e.x)            // page coordinates
 console.log(e.y)
 console.log(e.clientX)      // viewport coordinates, what getBoundingClientRect answers in
 console.log(e.clientY)
 console.log(e.relativeX)
 console.log(e.relativeY)
 console.log(e.xPercentage)
 console.log(e.yPercentage)
 console.log(e.velocityX)
 console.log(e.velocityY)
 console.log(e.prevX)
 console.log(e.prevY)
 console.log(e.pointerType)  // 'mouse', 'touch' or 'pen'
})

// a handle running along a track: 0-100 along the track, not along the handle, gliding along
// it alone after a flick capped at half a per cent of the track per millisecond
drag(handle, { within: track, inertia: true, axis: 'x', maxVelocity: '0.5%', callback: (d) => setPosition(d.xPercentage) })

// one delegated listener over a list, whatever the list does next
list.addEventListener('pointerdown', (e) => {
  const handle = e.target.closest('[data-handle]')
  if (handle) drag(e, { target: handle, callback: (d) => reorder(d.clientY) })
})

element.addEventListener('drag', (e) => { ... })
element.addEventListener('dragstart', (e) => { ... })
element.addEventListener('dragend', (e) => { ... })
element.addEventListener('dragcancel', (e) => { ... })
element.addEventListener('draginertia', (e) => { ... })
element.addEventListener('draginertiaend', (e) => { ... })

(static) encodeHTML(html) → {string}

Encodes HTML entities in a string using the browser's DOMParser. If the DOMParser is not available, it uses a regular expression to encode the basic entities.

Source:
See:
  • module:parsers.encodeHtmlEntities
Parameters:
Name Type Description
html string

The HTML string to encode

Returns:
Type:
string

The encoded HTML string

Example
encodeHTML('<div>foo</div>') // => '&lt;div&gt;foo&lt;/div&gt;'
encodeHTML('<div>foo</div><div>bar</div>') // => '&lt;div&gt;foo&lt;/div&gt;&lt;div&gt;bar&lt;/div&gt;'

(static) getFocusableElements(fromopt) → {Array.<HTMLElement>}

Returns all focusable elements from a given element or the document. Focusable elements are those that can be focused by the user, such as links, buttons, inputs, etc. Always use getVisibleFocusableElements instead of this function to ensure that only visible elements are returned, since only visible elements can be focused by the user.

Source:
See:
  • getVisibleFocusableElements
Parameters:
Name Type Attributes Default Description
from HTMLElement | Element | Document <optional>
document

The element to get the focusable elements from

Returns:
Type:
Array.<HTMLElement>

An array of focusable elements

Example
document.body.innerHTML = `
<div id="foo" tabindex="0">Foo</div>
<div id="bar" tabindex="-1">Bar</div>
<button id="baz">Baz</button>
<div id="qux" contenteditable="true">Qux</div>`
<a href="#quux" id="quux">Quux</a>

getFocusableElements() // => [<div id="foo" tabindex="0">Foo</div>, <button id="baz">Baz</button>, <div id="qux" contenteditable="true">Qux</div>, <a href="#quux" id="quux">Quux</a>]

(static) getHorizontalScrollState(element, thresholdopt) → {object}

Gets if the horizontal scroll has reached the start or end of the element.

Source:
Parameters:
Name Type Attributes Default Description
element HTMLElement

The element to check

threshold number <optional>
0

The threshold in pixels to consider the scroll as at the start or end

Returns:
Type:
object

An object with atStart and atEnd properties indicating if the scroll is at the start or end of the element

Example
const el = document.getElementById('foo')
const scrollState = getHorizontalScrollState(el, 10)
console.log(scrollState.atStart) // => true or false
console.log(scrollState.atEnd) // => true or false

(static) getTableData(selector, headers, rowSelectoropt, cellSelectoropt) → {Array.<object>}

Gets table data from a table element, a simple regular table element, or a table like structure. Useful for scraping data.

Source:
Parameters:
Name Type Attributes Default Description
selector string

The selector to select the table element

headers Array.<string> | string | null

The headers to use for the data. If 'auto' is passed, the row containing th or the first row will be used as headers

rowSelector string <optional>
'tr'

The selector to select the rows

cellSelector string <optional>
'td'

The selector to select the cells

Returns:
Type:
Array.<object>

An array of objects with the properties as keys and the cell values as values

Example
document.body.innerHTML = `
<table id="table">
 <thead>
  <tr>
  <th>Foo</th>
 <th>Bar</th>
</tr>
</thead>
<tbody>
<tr>
<td>Foo 1</td>
<td>Bar 1</td>
</tr>  
<tr>
<td>Foo 2</td>
<td>Bar 2</td>
</tr>
</tbody>
</table>`

getTableData('#table', ['foo', 'bar'])
// => [
//  { foo: 'Foo 1', bar: 'Bar 1' },
//  { foo: 'Foo 2', bar: 'Bar 2' }
// ]

(static) getTransitionDuration(element, propertyopt) → {number}

Returns the duration a single property will actually transition with, in milliseconds.

Unlike reading getTransitionDurations() by key, this resolves the all keyword: a transition: all 1s animates height too, it just isn't listed under that name. An untransitioned property reads as 0, which is also what transition: none gives you - so a reduced motion media query switching the transition off reads as "no animation".

Source:
Parameters:
Name Type Attributes Default Description
element HTMLElement

The element to get the transition duration from

property string <optional>
'all'

The CSS property to get the duration for

Returns:
Type:
number

The duration in milliseconds, 0 if the property is not transitioned

Example
getTransitionDuration(element, 'height') // 1000 if transition in CSS is set to 'height 1s'
getTransitionDuration(element, 'height') // 300 if transition in CSS is set to 'all 0.3s'
getTransitionDuration(element, 'height') // 0 if transition in CSS is set to 'opacity 1s'

(static) getTransitionDurations(element) → {object.<string, number>}

Returns a map of transition properties and durations

Source:
Parameters:
Name Type Description
element HTMLElement

The element to get the transition properties and durations from

Returns:
Type:
object.<string, number>

A map of transition properties and durations

Example
getTransitionDurations(element) // { height: 1000 } if transition in CSS is set to 'height 1s'
getTransitionDurations(element) // { height: 500, opacity: 1000 } if transition in CSS is set to 'height 0.5s, opacity 1s'

(static) getVerticalScrollState(element, thresholdopt) → {object}

Gets if the vertical scroll has reached the start or end of the element.

Source:
Parameters:
Name Type Attributes Default Description
element HTMLElement

The element to check

threshold number <optional>
0

The threshold in pixels to consider the scroll as at the start or end

Returns:
Type:
object

An object with atStart and atEnd properties indicating if the scroll is at the start or end of the element

Example
const el = document.getElementById('foo')
const scrollState = getVerticalScrollState(el, 10)
console.log(scrollState.atStart) // => true or false
console.log(scrollState.atEnd) // => true or false

(static) getVisibleFocusableElements(fromopt, excludeSelectoropt) → {Array.<HTMLElement>}

Returns all visible focusable elements from a given element or the document.

Empty where there is no layout to consult, since isVisible decides every row.

Source:
See:
  • getFocusableElements
  • isVisible
Parameters:
Name Type Attributes Default Description
from HTMLElement | Element | Document <optional>
document

The element to get the focusable elements from

excludeSelector string <optional>

A selector to exclude elements from the result

Returns:
Type:
Array.<HTMLElement>

An array of visible focusable elements

Example
document.body.innerHTML = `
<div id="foo" tabindex="0">Foo</div>
<div id="bar" tabindex="-1">Bar</div>
<button id="baz" style="display: none;">Baz</button>
<div id="qux" contenteditable="true">Qux</div>`

getVisibleFocusableElements() // => [<div id="foo" tabindex="0">Foo</div>, <div id="qux" contenteditable="true">Qux</div>]
getVisibleFocusableElements(document.body, '#foo') // => [<div id="qux" contenteditable="true">Qux</div>]

(static) insertBeforeElement(targetElement, newElement)

Inserts an element before another element

Source:
Parameters:
Name Type Description
targetElement HTMLElement

The element to insert before

newElement HTMLElement

The element to insert

Example
const target = document.getElementById('target')
const newElement = document.createElement('div')
newElement.id = 'newElement'
insertBeforeElement(target, newElement)
// <div id="newElement"></div>
// <div id="target"></div>

(static) isEmptyElement(element)

Checks if an element is empty

Source:
Parameters:
Name Type Description
element HTMLElement
Returns:

boolean

Example
document.body.innerHTML = `
 <div id="empty-element"></div>
 <div id="non-empty-element1">foo</div>
 <div id="non-empty-element2"><br></div>`

isEmptyElement(document.getElementById('empty-element')) // => true
isEmptyElement(document.getElementById('non-empty-element1')) // => false
isEmptyElement(document.getElementById('non-empty-element2')) // => false

(static) isHorizontalScrollVisible(element) → {boolean}

Checks if an element is overflowing horizontally used to check if the scrollbar is visible.

Source:
Parameters:
Name Type Description
element HTMLElement

The element to check

Returns:
Type:
boolean

True if the element is overflowing, false otherwise

Example
const el = document.getElementById('foo')
isHorizontalScrollVisible(el) // => true or false

(static) isScrollVisible(element) → {boolean}

Checks if an element is overflowing its container either vertically or horizontally used to check if the scrollbar is visible.

Source:
Parameters:
Name Type Description
element HTMLElement

The element to check

Returns:
Type:
boolean

True if the element is overflowing, false otherwise

Example
const el = document.getElementById('foo')
isScrollVisible(el) // => true or false

(static) isVerticalScrollVisible(element) → {boolean}

Checks if an element is overflowing its container used to check if the scrollbar is visible.

Source:
Parameters:
Name Type Description
element HTMLElement

The element to check

Returns:
Type:
boolean

True if the element is overflowing, false otherwise

Example
const el = document.getElementById('foo')
isVerticalScrollVisible(el) // => true or false

(static) isVisible(element) → {boolean}

If provided element is visible. Checks if the element is not visibility hidden or display none, has no opacity, and has a width and height.

Answers false for everything where there is no layout to consult - jsdom, and any other DOM without a renderer behind it - because the fallback path ends on the element having a box, and nothing in such an environment ever has one. That is honest rather than useful: a DOM that renders nothing cannot say what is visible. Code that has to run under one wants a narrower question it can actually answer - whether the element sits inside a [hidden] ancestor, say.

Source:
Parameters:
Name Type Description
element HTMLElement

The element to check

Returns:
Type:
boolean

True if the element is visible, false otherwise

Example
isVisible(document.getElementById('foo'))

(static) loadImage(src, callbackopt)

Loads an image form a provided source url and calls a callback when it's loaded

Source:
Parameters:
Name Type Attributes Description
src string

The source url of the image

callback function <optional>

The callback to call when the image is loaded

Example
loadImage('https://example.com/image.png', () => {
 console.log('Image loaded')
})

(static) matchesAll(elements, selector) → {boolean}

Check a list of elements if all of them matches a selector

Source:
Parameters:
Name Type Description
elements Array.<HTMLElement> | NodeList | HTMLElement

The elements to check

selector string

The selector to check

Returns:
Type:
boolean

True if all of the elements matches the selector, false otherwise

Example
document.body.innerHTML = `
<div id="foo"></div>
<div id="bar"></div>
<div id="baz"></div>`

matchesAll(document.querySelectorAll('div'), 'div') // => true
matchesAll(document.querySelectorAll('div'), '#foo') // => false

(static) matchesAny(elements, selector) → {boolean}

Check a list of elements if any of them matches a selector

Source:
Parameters:
Name Type Description
elements Array.<HTMLElement> | NodeList | HTMLElement

The elements to check

selector string

The selector to check

Returns:
Type:
boolean

True if any of the elements matches the selector, false otherwise

Example
document.body.innerHTML = `
<div id="foo"></div>
<div id="bar"></div>
<div id="baz"></div>`

matchesAny(document.querySelectorAll('div'), '#foo') // => true
matchesAny(document.querySelectorAll('div'), '#qux') // => false

(static) on(selector, eventTypeOrHandler, handleropt) → {MutationObserver|Object|null}

Run a handler on selected elements and on elements added to the DOM with the same selector as a MutationObserver abstraction, or use it to delegate events as a delegateEvent alias

Source:
See:
  • delegateEvent
Parameters:
Name Type Attributes Description
selector string

The selector to select the elements to run the handler on

eventTypeOrHandler string | function

The event type to delegate, like click, or the handler to call on every element

handler function <optional>

The handler to call when the event is triggered.

Returns:
Type:
MutationObserver | Object | null

The MutationObserver instance (with an added destroy() method), a destroy/disconnect handle for bubbling delegated events, or null when MutationObserver is required but unavailable

Example
on('.foo', (el) => {
console.log('Element', el, 'added to the DOM')
})

on('.foo', 'click', (e, target) => {
console.log('Clicked on', target)
})

(static) parseDOM(html, allChildrenopt) → {Node}

Parses HTML string to a DOM Node

Source:
Parameters:
Name Type Attributes Default Description
html string

The HTML string to parse

allChildren boolean <optional>
false

If true, all children of the body will be returned, otherwise only the first child

Returns:
Type:
Node

The parsed DOM Node

Example
parseDOM('<div>foo</div>') // => <div>foo</div>
parseDOM('<div>foo</div><div>bar</div>', true) // => NodeList(2) [div, div]
parseDOM(document.getElementById('foo')) // => <div id="foo"></div>
parseDOM(document.querySelectorAll('div')) // => NodeList(2) [div, div]

(static) proportionalParentCoverResize(elements, ratioopt, offsetopt)

Resizes an element to cover its parent element while maintaining the aspect ratio

Source:
Parameters:
Name Type Attributes Default Description
elements string | HTMLElement | NodeList

The elements or a selector for elements to resize

ratio number <optional>
1

The ratio to maintain

offset number <optional>
0

An offset to add to the parent element's width and height

Example
proportionalParentCoverResize('.foo', 16/9, 10)

(static) query(selector, fromopt) → {Array.<Element>|NodeList}

Queries the DOM for elements and returns them. Substitutes for document.querySelectorAll(selector) and JQuery's $(selector)

Source:
Parameters:
Name Type Attributes Default Description
selector string | HTMLElement | Element | Array.<(HTMLElement|Element)> | NodeList

The selector to select elements

from HTMLElement | Element <optional>
document

The element to query from

Returns:
Type:
Array.<Element> | NodeList
Example
document.body.innerHTML = `
<div id="foo"></div>
<div id="bar"></div>
<div id="baz"></div>`

query('#foo') // => [<div id="foo"></div>]
query(document.getElementById('foo')) // => [<div id="foo"></div>]
query('div') // => [<div id="foo"></div>, <div id="bar"></div>, <div id="baz"></div>]

(static) querySingle(selector, fromopt) → {HTMLElement|Element}

Queries the DOM for a single element and returns it. Substitutes for document.querySelector(selector) and JQuery's $(selector).first()

Source:
Parameters:
Name Type Attributes Default Description
selector string | HTMLElement | Element | Array.<(HTMLElement|Element)> | NodeList

The selector to select an element

from HTMLElement | Element <optional>
document

The element to query from

Returns:
Type:
HTMLElement | Element
Example
document.body.innerHTML = `
<div id="foo"></div>
<div id="bar"></div>
<div id="baz"></div>`

querySingle('#foo') // => <div id="foo"></div>
querySingle(document.getElementById('foo')) // => <div id="foo"></div>
querySingle(document.querySelector('#foo')) // => <div id="foo"></div>

(static) readOptions(element, schema) → {object}

Read a set of typed options off an element's attributes.

Accepts the bare, kebab-case and data-* spellings of each option, with data-* winning when both are present, so a widget can be configured whichever way the markup author reaches for. Only the options actually present come back - absent ones are left out rather than defaulted, so the result merges cleanly over whatever defaults the caller holds.

Booleans follow HTML, not JavaScript: a bare attribute parses as an empty string and means "on", and only false and 0 mean off. Numbers that do not parse are dropped rather than coming back as NaN.

Source:
Parameters:
Name Type Description
element HTMLElement

The element to read the attributes from

schema object.<string, ('boolean'|'number'|'string')>

A map of camelCase option names to types

Returns:
Type:
object

The options present on the element, typed

Example
// <div data-page-step="25" exclusive label="FAQ">
readOptions(element, { pageStep: 'number', exclusive: 'boolean', label: 'string', muted: 'boolean' })
// => { pageStep: 25, exclusive: true, label: 'FAQ' }

(static) remove(selector, fromopt)

Removes all elements matching a selector from the DOM

Source:
Parameters:
Name Type Attributes Default Description
selector string | HTMLElement | Element

The selector to select elements to remove

from HTMLElement | Element <optional>
document

The element to remove elements from

Example
document.body.innerHTML = `
<div id="foo"></div>
<div id="bar"></div>
<div id="baz"></div>`
`
remove('#foo, #bar') // => removes #foo and #bar

(static) removeListenerForEvents(elements, events, handler, optionsopt)

Removes one listener from multiple registered events

Source:
Parameters:
Name Type Attributes Description
elements string | HTMLElement | NodeList

The elements or a selector for elements to remove the event listeners from

events string | Array.<string>

The event types to remove the event listeners for, like click mouseenter

handler function

The handler to remove

options object <optional>

The options to pass to the event listeners

Example
removeListenerForEvents('.foo', 'click mouseenter', (e) => { console.log(e.type) })

(static) scrollSpy(elements, callback, optionsopt) → {object|null}

Reports which section the reader is currently in, and calls back when that changes.

The current section is the last one whose top edge has passed the reading line — offset pixels below the top of the viewport — which is not the same as the topmost section on screen: a heading scrolled just out of sight is still the section being read. At the very bottom of the page the last section wins outright, because a final section shorter than the screen can never reach the line and would otherwise be unreachable. Above the first section the callback gets null, leaving what the top of the page means to the caller.

Driven by a rAF-throttled scroll listener rather than an IntersectionObserver: an observer fires only when visibility changes, so scrolling from one heading to the next inside a screen that already shows both tells it nothing, and it keeps reporting the section before.

Section positions are measured once and cached, so a scrolled frame costs one layout read — a document-height check — instead of one per section. The cache rebuilds itself on window resize, through a ResizeObserver on document.body whenever the body changes size, and on any scrolled frame where the document's height moved — which is what catches a body pinned to 100% height, whose box never resizes while its content grows. An IntersectionObserver over the sections rides along as a staleness probe: its entries carry each section's rectangle for free, so a shift that changed no height anywhere — two sections trading equal heights, a transform settling — is caught the moment a moved section crosses a viewport edge. A shift that neither changes a height nor crosses an edge answers stale until update() is called; each observer degrades alone, so a browser missing one keeps every other trigger.

Follows the window's scroll only, not a scrolling container's.

Source:
Parameters:
Name Type Attributes Description
elements string | Element | Array.<Element> | NodeList

The sections to watch, or a selector for them

callback function

Called with the current section and its index, or with null and -1

options object <optional>
Name Type Attributes Default Description
offset number | function <optional>
0

Pixels below the top of the viewport that count as the reading line — the height of a sticky header, usually. A function is read every frame, so a header that collapses mid-scroll keeps the line honest

Returns:
Type:
object | null

{ update, destroy }, or null when there is no callback or nothing to watch

Example
const spy = scrollSpy('.prose h2[id]', (section) => {
 document.querySelectorAll('.toc a').forEach((a) => a.removeAttribute('aria-current'))
 if (section) document.querySelector(`.toc a[href="#${section.id}"]`)?.setAttribute('aria-current', 'location')
}, { offset: 64 })

spy.update()  // re-measure after a layout change the observers cannot see
spy.destroy() // stop listening

(static) swipe(element, callback, thresholdopt, timeThresholdopt, mouseopt) → {object|null}

Swipe event handler

Two points make the gesture - where the pointer went down and where it came up - so there is no pointermove listener running on every frame of every scroll down the page. The axis it travelled furthest along is the one reported, and a gesture as far down as across is neither.

Source:
Parameters:
Name Type Attributes Default Description
element HTMLElement

The element to listen for swipe gestures on

callback object | function

The callback to call when a swipe gesture is detected or the options object with the callback, threshold, timeThreshold and mouse

threshold number <optional>
150

The threshold in pixels to trigger the callback.

timeThreshold number <optional>
0

The threshold in milliseconds to trigger the callback. Defaults to 0, which means the callback will be called regardless of the time it took to swipe.

mouse boolean <optional>
true

Whether a mouse drag counts as a swipe. Options object only. Turn it off on anything with selectable text, draggable images or links in it - reading a swipe from a mouse costs the page all three.

Returns:
Type:
object | null

The destroy method to remove the event listeners, or null without an element

Example
swipe(document.getElementById('foo'), (e) => {
 console.log(e.direction)
 console.log(e.deltaX)
 console.log(e.deltaY)
 console.log(e.startX)
 console.log(e.startY)
 console.log(e.endX)
 console.log(e.endY)
 console.log(e.threshold)
 console.log(e.type)
 console.log(e.target)
 console.log(e.horizontal)
 console.log(e.vertical)
 console.log(e.horizontalDirection)
 console.log(e.verticalDirection)
 console.log(e.timeElapsed)
 console.log(e.timeThreshold)
})

swipe(document.getElementById('foo'), { callback: onSwipe, threshold: 40, mouse: false })

element.addEventListener('swipe', (e) => { ... })
element.addEventListener('swipestart', (e) => { ... })
element.addEventListener('swipeend', (e) => { ... })

(static) toggleAttributeValue(element, attribute, on, off)

Toggles an attribute value on an element

Source:
Parameters:
Name Type Description
element HTMLElement

The element to toggle the attribute on

attribute string

The attribute to toggle

on string

Default: 'true'

off string

Default: 'false'

Example
toggleAttributeValue(element, 'aria-expanded', 'true', 'false')
toggleAttributeValue(element, 'aria-expanded')

(static) whyNotSticky(targetopt) → {Array.<object>}

Reports why a sticky element cannot stick.

position: sticky fails silently: no error, no warning, an element that simply never moves. The usual cause is an ancestor with overflow: hidden, scroll, auto or overlay, which becomes the scrollport the element sticks inside — and when that ancestor does not itself scroll, there is nowhere for the element to go. No DevTools reports it: neither engine's inactive-CSS pass has a rule for sticky, since the declaration is valid and applied, and their scroll badges mark containers that really do scroll — where the one that breaks stickiness is a box that never scrolls at all. This reads the ancestors and names the culprit.

Each finding carries a code, the element it is about, the culprit element to look at, the problem in one sentence and a fix. The codes are detached (not in the document, so nothing about it computes), not-sticky, no-inset (every inset is auto, so there is no threshold to stick at), no-room (the containing block is no larger than the element), dead-scrollport (an ancestor is the scrollport and never scrolls) and nested-scrollport (the element sticks inside an ancestor rather than to the viewport, which may well be what you meant).

An empty array means nothing here can see a reason, never that the element sticks. Three things it cannot see: contain: paint, contain: layout and content-visibility on an ancestor; html and body overflow, skipped because a non-visible value there propagates to the viewport and reporting it would fire on the most ordinary markup there is; and a containing block that is not the element's parent, since no-room measures the parent.

Source:
Parameters:
Name Type Attributes Description
target string | Element | Array.<Element> | NodeList <optional>

The element or elements to diagnose, or a selector for them. Left out, every sticky element on the page is diagnosed

Returns:
Type:
Array.<object>

One finding per problem found, [] when none were

Examples
// In the console: every sticky element on the page, and what is wrong with each
console.table(whyNotSticky())
whyNotSticky('#sidebar')
// => [{ code: 'dead-scrollport', element: aside#sidebar, culprit: div.wrap,
//       problem: 'this ancestor is the scrollport (`overflow-y: hidden`) and never scrolls...',
//       fix: '`overflow-y: clip` clips the same and creates no scrollport' }]