A lightweight, functional DOM manipulation library with hybrid calling patterns and full TypeScript support. Think jQuery meets functional programming with modern TypeScript ergonomics - use imperative, curried, or fluent styles!
Composability - Functions are small, focused, and combine naturally:
import { find, modify, addClass, on } from '@doeixd/dom';
// Compose operations in a pipeline
const setupButton = (selector: string) =>
pipe(
find(selector),
modify({ text: 'Click me!' }),
addClass('btn', 'btn-primary'),
on('click', handleClick)
);Currying - Partial application makes code reusable:
import { modify, addClass } from '@doeixd/dom';
// Create specialized functions
const makeActive = addClass('active');
const setText = (text: string) => modify({ text });
// Reuse across elements
[button1, button2, button3].forEach(makeActive);Null Safety - All functions handle null gracefully:
import { modify } from '@doeixd/dom';
const missing = document.querySelector('.nonexistent');
modify(missing)({ text: 'Hello' }); // No error, safely returns nullImmutability - No hidden state or side effects:
import { css } from '@doeixd/dom';
// Pure function - same inputs = same outputs
const redText = css({ color: 'red' });
redText(element1); // Predictable
redText(element2); // ReusableChoose your style - imperative convenience or functional composition:
import { modify, addClass, css } from '@doeixd/dom';
const button = document.querySelector('button');
// π
°οΈ Imperative (clean & direct)
modify(button, { text: 'Click me!' });
addClass(button, 'primary', 'large');
css(button, { color: 'blue' });
// π
±οΈ Curried (pipeline-friendly)
modify(button)({ text: 'Click me!' });
addClass(button)('primary', 'large');
css(button)({ color: 'blue' });
// π
²οΈ Fluent (jQuery-like chaining)
$(button)
.modify({ text: 'Click me!' })
.addClass('primary', 'large')
.css({ color: 'blue' });All patterns work everywhere - mix and match based on context!
npm install @doeixd/domUse directly in the browser without npm or build tools:
<script type="module">
// Import only what you need - automatic tree-shaking
import { find, modify, on } from 'https://esm.sh/@doeixd/dom';
const button = find('button');
modify(button)({ text: 'Click me!' });
on(button)('click', () => alert('Clicked!'));
</script>Tree-Shaking with ESM.sh: Use the ?exports parameter to bundle only specific functions:
<script type="module">
// Only bundles find, modify, and on (~2KB instead of ~15KB)
import { find, modify, on } from 'https://esm.sh/@doeixd/dom?exports=find,modify,on';
</script>import { find, modify, addClass, on } from '@doeixd/dom';
// Direct, clean calls - no extra parentheses!
const button = find('button');
modify(button, { text: 'Submit' });
addClass(button, 'btn-primary');
on(button, 'click', handleSubmit);import { find, modify, addClass, on } from '@doeixd/dom';
// Curried functions for composition
const button = find('button');
modify(button)({ text: 'Submit' });
addClass(button)('btn-primary');
on(button)('click', handleSubmit);import { $ } from '@doeixd/dom';
// Method chaining
$('button')
.modify({ text: 'Submit' })
.addClass('btn-primary')
.on('click', handleSubmit);import { find, findAll, closest } from '@doeixd/dom';
// Type-safe selectors
const button = find<HTMLButtonElement>('button');
const inputs = findAll<HTMLInputElement>('input');
const form = closest(button)('form');import { el, html } from '@doeixd/dom';
// Functional element builder
const button = el('button')({ class: { primary: true } })(['Click me']);
// Template literals (auto-detects single vs multiple roots)
const single = html`<div>Hello</div>`; // HTMLElement
const multiple = html`<div>A</div><div>B</div>`; // DocumentFragmentimport { on, onDelegated } from '@doeixd/dom';
// Direct events
const unsub = on(button)('click', (e) => console.log('Clicked'));
// Event delegation (performance++)
const onRow = onDelegated(table)('tr');
onRow('click', (e, row) => console.log('Row clicked', row));import { Http } from '@doeixd/dom';
// Create configured client
const api = Http.create({
baseURL: 'https://api.example.com',
headers: { 'Authorization': 'Bearer token' },
timeout: 5000
});
// Type-safe requests
interface User { id: number; name: string; }
const user = await api.get<User>('/users/1');
await api.post('/users', { name: 'John' });import { Form, Input } from '@doeixd/dom';
// Serialize form data
const data = Form.serialize(form);
// Watch input changes
Input.watch(input)((value) => console.log('Value:', value));
// Validation
const isValid = Input.validate(input);import { find, modify, addClass, on } from '@doeixd/dom';
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
const setupButton = pipe(
modify({ text: 'Submit' }),
addClass('btn-primary'),
on('click', handleClick)
);
setupButton(find('button'));import { modify, css } from '@doeixd/dom';
// Create reusable modifiers
const setLoading = modify({
text: 'Loading...',
disabled: true
});
const setSuccess = modify({
text: 'Success!',
disabled: false
});
// Apply to any button
setLoading(submitBtn);
// Later...
setSuccess(submitBtn);import { findAll, addClass } from '@doeixd/dom';
// No intermediate variables needed
findAll('.item')
.map(addClass('active'))
.forEach(on('click', handleClick));Choose the style that fits your needs - all functions support multiple patterns:
import { modify, addClass, css } from '@doeixd/dom';
// Clean, straightforward calls
modify(button, { text: 'Click' });
addClass(button, 'primary');
css(button, { color: 'blue' });import { modify, addClass, css } from '@doeixd/dom';
// Perfect for composition and pipelines
modify(button)({ text: 'Click' });
addClass(button)('primary');
css(button)({ color: 'blue' });import { $ } from '@doeixd/dom';
// jQuery-like method chaining
$('button')
.modify({ text: 'Click' })
.addClass('primary')
.css({ color: 'blue' });import { def } from '@doeixd/dom';
// Create your own hybrid functions
const setText = def((el, text) => el.innerText = text);
setText(button, 'Click'); // Direct call
setText(button)('Click'); // Curried call- Modern browsers (ES2020+)
- Chrome 80+
- Firefox 75+
- Safari 13.1+
- Edge 80+
- Full library: ~15KB minified + gzipped
- Tree-shaken: As small as 1-2KB for basic utilities
- Zero dependencies
- TypeScript-first: Includes full type definitions
Advanced TypeScript support with full type safety and intelligent inference:
import { find, modify, addClass, on } from '@doeixd/dom';
// π― Smart type inference from selectors
const button = find('button'); // HTMLButtonElement
const input = find('input'); // HTMLInputElement
const form = find('form'); // HTMLFormElement
// π§ Generic support for custom elements
const custom = find<MyCustomElement>('.custom');
// β‘ Function overloads for hybrid patterns
modify(button, { text: 'Click' }); // Imperative
modify(button)({ text: 'Click' }); // Curried
// πͺ Event type safety
on(button, 'click', (e) => {
// e is MouseEvent - fully typed!
console.log(e.clientX, e.clientY);
});
// π Data attributes with type inference
Data.set(button, 'user-id', 123); // Accepts any serializable type
const userId = Data.read(button)('user-id'); // Returns any (parsed JSON/number/string)Key TypeScript Features:
- β
Strict mode compliant - no
anyabuse - β Function overloads - both imperative and curried patterns
- β HTMLElement generics - type-safe element operations
- β EventMap inference - proper event typing
- β
Null safety - handles
null/undefinedgracefully - β Advanced generics - complex type constraints where needed
Power your own hybrid functions with the def utility:
import { def } from '@doeixd/dom';
// Create functions that work both ways
const setText = def((el, text) => el.innerText = text);
const addClasses = def((el, ...classes) => el.classList.add(...classes));
// Use imperatively
setText(button, 'Hello World');
addClasses(div, 'active', 'visible');
// Use in pipelines
pipe(
find('.button'),
setText('Click me'),
addClasses('primary', 'large')
);How it works:
// def() creates dual-callable functions
const fn = def((target, ...args) => result);
// Imperative: fn(target, ...args)
fn(element, arg1, arg2);
// Curried: fn(target)(...args)
fn(element)(arg1, arg2);MIT Β© Patrick Glen