Lightweight module to monitor DOM elements matching a CSS selector
npm install animation-observer
import {observe} from 'animation-observer'
observe('div', (element) => {
console.log(element.id, 'just slid into the DOM.')
})
declare function observe(
selector: string | string[],
initialize: (element: Element) => void,
options?: {
event?: 'start' | 'end' | 'cancel'
duration?: string
signal?: AbortSignal
name?: string
},
): AbortController
You can pass a AbortSignal
to the signal
option.
const controller = new AbortController()
observe('img', () => {
console.log('An image just showed up!')
}, {
signal: controller.signal,
})
controller.abort()
By default, the function listens to the animationstart
event, which triggers when a matching element "appears".
You can listen to a different event
in the options. The most prominent usage is to check element "disappears":
observe('div', (element) => {
console.log(element.id, 'left the party.')
}, {
event: 'cancel', // `animationcancel`
})
Or execute a function after a duration
:
observe('input:focus', () => {
alert('Please fill in your answer quickly.')
}, {
event: 'end', // `animationend`
duration: '10s',
})
duration
has different meaning depending on event
:
- For
start
, it does nothing - For
end
, it is a delay - For
cancel
, it is a timeout (default:9999s
a.k.a "infinity")
- By "appearing", it means
animationstart
andanimationend
events are fired when the element meets the following conditions:- Element matches the selector
- Element is not
display: none
(visibility: hidden
is fine)
- By "disappearing", it means
animationcancel
event is fired when one of the following happens:- Element no longer matches the selector (may still be in the DOM)
- Element is removed from the DOM
- Element becomes
display: none
By default, the function generates a random class name using crypto.randomUUID()
.
You can specify a custom name
in the options:
observe('[href="https://www.random.org/"]', () => {
console.log('True randomness™️')
}, {
name: Math.random().toString(36).slice(2),
})
This module uses CSS @layer
to avoid conflicting with existing styles, which is supported since:
- Chrome & Edge 99
- Firefox 97
- Safari 15.4
The first prototype is inspired by @fregante.
Support for multiple listeners matching the same element in v2.1.0 is adapted from Refined GitHub.