@briantbailey/dom-once
v1.0.0
Published
Simple utilities to select and manipulate DOM elements only once.
Maintainers
Readme
@briantbailey/dom-once
Simple utilities to select and manipulate DOM elements only once.
Run your DOM initialization code safely, even when called multiple times. Ideal for SPAs and dynamic DOM updates. Framework-agnostic; safe after re-renders or partial updates.
// Initialize buttons only once, even if called multiple times
doOnce('btn-init', '.btn', (btn) => btn.classList.add('ready'));Features
- Zero dependencies — Pure DOM utilities, no external libraries
- Enforces idempotent initialization — Prevents re-execution on already-processed elements
- Flexible selectors — Works with CSS strings, Elements, NodeLists, or arrays
- Customizable tracking — Use any data attribute name
- Framework-agnostic — Pure JavaScript, works anywhere the DOM exists
- Full TypeScript support — Complete type definitions included
- Tree-shakeable ESM — Modern bundlers can eliminate unused code
- Tiny footprint — Check current bundle size
How It Works
Tracks processed elements using a data attribute (default: data-dom-once) with space-delimited tokens:
<button data-dom-once="btn-init tooltip analytics">Click me</button>Each once id marks that a specific operation has been performed, preventing duplicate initialization even when your code runs multiple times.
OnceId Rules:
- Valid characters: letters, numbers, underscores (
_), and hyphens (-) - Examples:
btn-init,tooltip_setup,v2 - Avoid spaces (tokens are space-delimited)
- Case-sensitive:
myIdandmyidare different
Installation
Package Managers
npm / pnpm / yarn:
npm install @briantbailey/dom-onceJSR (Node or Deno):
npx jsr add @briantbailey/dom-once
# or
deno add @briantbailey/dom-onceCDN
esm.sh:
Import directly as ESM in browsers or Deno:
https://esm.sh/@briantbailey/dom-onceunpkg:
Access package files directly:
https://unpkg.com/@briantbailey/dom-once@latest/dist/dom-once.js (ESM)
https://unpkg.com/@briantbailey/dom-once@latest/dist/dom-once.iife.min.js (IIFE)GitHub Releases:
Download the IIFE bundle directly from releases.
Usage
Node / Deno (ESM)
import { doOnce, querySelectorOnce, removeOnce, findOnce, version } from '@briantbailey/dom-once';
doOnce('btn-init', '.btn', (btn) => {
btn.classList.add('initialized');
});Note: This package is ESM-only. Use import in Node and bundlers; require() is not supported.
Browser (ESM via CDN)
Static import:
<script type="module">
import { doOnce } from 'https://esm.sh/@briantbailey/dom-once';
doOnce('btn-init', '.btn', (btn) => btn.classList.add('ready'));
</script>Dynamic import:
<script>
import('https://esm.sh/@briantbailey/dom-once')
.then(({ doOnce }) => {
doOnce('btn-init', '.btn', (btn) => btn.classList.add('ready'));
});
</script>Browser (IIFE)
The IIFE bundle exposes everything on the global domOnce object:
<script src="https://unpkg.com/@briantbailey/dom-once@latest/dist/dom-once.iife.min.js"></script>
<script>
const { doOnce, querySelectorOnce, removeOnce, findOnce, version } = window.domOnce;
doOnce('btn-init', '.btn', (btn) => btn.classList.add('ready'));
</script>API
querySelectorOnce(onceId, selector[, options]) → Element[]
Queries for elements using a CSS selector and marks them with a once id.
onceId:string— Unique identifier (alphanumeric, underscore, hyphen)selector:string— CSS selector stringoptions:object(optional)onceAttribute:string— Data attribute name (default:'data-dom-once')context:Document | DocumentFragment | Element— Query context (default:document)
- Returns:
Element[]— Elements that matched the selector AND didn't already have the once id (i.e., newly marked). Elements already marked are excluded.
doOnce(onceId, selector, callback[, options]) → Element[]
Executes a callback once per element, marking elements to prevent re-execution.
onceId:string— Unique identifierselector:string | Element | Iterable<Element> | ArrayLike<Element>— Elements to processcallback:(element: Element) => void— Function to execute on each unmarked elementoptions:object(optional)onceAttribute:string— Data attribute name (default:'data-dom-once')context:Document | DocumentFragment | Element— Query context (default:document)
- Returns:
Element[]— Elements that were newly processed (matched the selector and didn't already have the once id). Elements already marked are excluded.
removeOnce(onceId, selector[, options]) → Element[]
Removes a once id from elements.
onceId:string— Unique identifier to removeselector:string | Element | Iterable<Element> | ArrayLike<Element>— Elements to processoptions:object(optional)onceAttribute:string— Data attribute name (default:'data-dom-once')context:Document | DocumentFragment | Element— Query context (default:document)
- Returns:
Element[]— Only elements that actually had the once id removed (matched the selector AND had the once id). Unmarked elements are excluded.
findOnce(onceId[, options]) → Element[]
Finds all elements marked with a specific once id (read-only).
onceId:string— Unique identifier to search foroptions:object(optional)onceAttribute:string— Data attribute name (default:'data-dom-once')context:Document | DocumentFragment | Element— Search context (default:document)
- Returns:
Element[]— Elements marked with the once id
version
string — Library version (e.g., "1.0.0")
Development
This project uses pnpm for package management.
pnpm install # Install dependencies
pnpm run check # Lint, typecheck, and test
pnpm run build # Build for production
pnpm run test # Run testsLicense
MIT © 2025 Brian T. Bailey
