Deterministic DOM element getters by ID — typed, tiny, modern.
id-dom is a small utility for grabbing DOM references safely by id, with predictable behavior:
- Typed getters like
button('saveBtn'),input('nameInput'),svg('icon') - Strict or optional mode (
throwvsnull) - Short optional alias via
.opt - Scoped lookups for
document,ShadowRoot,DocumentFragment, or anElement - Centralized error handling with
onErrorand optionalwarn - Zero deps
This is deliberately not a selector framework. It is a tiny, ID-first primitive for safe DOM wiring.
npm install id-domimport dom from 'id-dom'
const saveBtn = dom.button('saveBtn')
saveBtn.addEventListener('click', save)Optional access never throws for missing or wrong-type elements:
const debug = dom.div.optional('debugPanel')
debug?.append('hello')
const maybeCanvas = dom.canvas.opt('game')Using getElementById is:
- fast
- unambiguous
- easy to reason about
And with typed getters, you immediately know whether you got a HTMLButtonElement, HTMLInputElement, SVGSVGElement, and so on.
When scoped roots do not support getElementById, id-dom falls back to querySelector(#id) and safely escapes edge-case IDs.
The default export is a scoped instance using document (when available) with strict behavior:
- missing element → throws
- wrong type or wrong tag → throws
- invalid input → throws
import dom from 'id-dom'
const name = dom.input('nameInput')
const submit = dom.button('submitBtn')Create a scoped instance that searches within a specific root:
document→ usesgetElementByIdShadowRoot,DocumentFragment, orElement→ usesquerySelector(#id)fallback
import { createDom } from 'id-dom'
const d = createDom(document, { mode: 'null', warn: true })
const sidebar = d.div('sidebar')type DomMode = 'throw' | 'null'
{
mode?: DomMode
warn?: boolean
onError?: (err: Error, ctx: any) => void
}Generic typed lookup:
import { byId } from 'id-dom'
const btn = byId('saveBtn', HTMLButtonElement)Optional variants:
const maybeBtn = byId.optional('saveBtn', HTMLButtonElement)
const maybeBtn2 = byId.opt('saveBtn', HTMLButtonElement)- valid match → returns the element
- missing element → throws or returns
null - wrong type → throws or returns
null - invalid
id→ throws or returnsnull - invalid
Type→ throws or returnsnull
Tag-based validation when constructor checks are not the right fit:
import { tag } from 'id-dom'
const main = tag('appMain', 'main')
const icon = tag('icon', 'svg', { root: container })Optional variants:
const maybeMain = tag.optional('appMain', 'main')
const maybeMain2 = tag.opt('appMain', 'main')- valid tag match → returns the element
- missing element → throws or returns
null - wrong tag → throws or returns
null - invalid
id→ throws or returnsnull - invalid
tagName→ throws or returnsnull
Available on dom and on any createDom() instance:
el(id)→HTMLElementinput(id)→HTMLInputElementbutton(id)→HTMLButtonElementtextarea(id)→HTMLTextAreaElementselect(id)→HTMLSelectElementform(id)→HTMLFormElementdiv(id)→HTMLDivElementspan(id)→HTMLSpanElementlabel(id)→HTMLLabelElementcanvas(id)→HTMLCanvasElementtemplate(id)→HTMLTemplateElementsvg(id)→SVGSVGElementbody(id)→HTMLBodyElement
Each getter also has:
dom.canvas.optional('game')
dom.canvas.opt('game')main(id)→ validates<main>section(id)→ validates<section>small(id)→ validates<small>
Each also supports .optional and .opt.
import dom from 'id-dom'
dom.button('missing') // throwsimport { createDom } from 'id-dom'
const d = createDom(document, { mode: 'null' })
d.button('missing') // nullconst d = createDom(document, {
mode: 'null',
onError: (err, ctx) => {
// sendToSentry({ err, ctx })
},
})Enable console warnings too:
createDom(document, { mode: 'null', warn: true })import { createDom } from 'id-dom'
const host = document.querySelector('#widget')
const shadow = host.attachShadow({ mode: 'open' })
shadow.innerHTML = `<button id="shadowBtn">Click</button>`
const d = createDom(shadow)
const btn = d.button('shadowBtn')const container = document.querySelector('#settings-panel')
const d = createDom(container)
const input = d.input('emailInput')const container = document.querySelector('#icons')
const d = createDom(container)
const icon = d.svg('logoMark')el(id)is specifically forHTMLElement, not every possible DOMElement.body(id)looks up a<body>by ID. This library stays ID-first on purpose.tag()can validate non-HTML tags too, such assvg, when used against supported scoped roots.
Modern browsers supporting:
getElementByIdquerySelector
CSS.escape is used when available. A safe internal fallback is included for environments such as some jsdom builds where it may be missing.
See LICENSE.