simpledom
Last updated
The simpledom library lets you easily access and manipulate the DOM in a typescript-oriented API.
Find it on GitHub: sufianrhazi/simpledom
Install it via: npm install @srhazi/simpledom
Motivation
While typescript is a fantastic language, the DOM APIs were not designed for type safety. For example, let's say you want to write a very simple todo list using typescript in strict mode.
Let's assume we have the following html:
<h2>Todo list</h2>
<div id="todo-app">
<form id="todo-add" action="">
<label>
New item:
<input type="text" name="item" />
</label>
<button type="submit" name="add">Add</button>
<button type="button" name="clear">
Clear Completed
</button>
</form>
<ul id="todo-list"></ul>
</div>
You could either use the raw DOM API, and be riddled with awkward type coercion/not-null assertions, and annoyingly verbose DOM construction:
const todoListEl = document.querySelector('#todo-list')! as HTMLElement;
const formEl = document.querySelector('#todo-add')! as HTMLElement;
const inputEl = formEl.querySelector('input[name="item"]')! as HTMLInputElement;
const addEl = formEl.querySelector('button[name="add"]')! as HTMLButtonElement;
const clearEl = formEl.querySelector('button[name="clear"]')! as HTMLButtonElement;
todoListEl.addEventListener('click', (event) => {
const target = event.target as Node;
for (let itemEl of Array.from(todoListEl.children)) {
if (itemEl.querySelector('.delete')!.contains(target)) {
todoListEl.removeChild(itemEl);
}
}
});
formEl.addEventListener('submit', event => {
event.preventDefault();
if (inputEl.value.trim().length > 0) {
const todoItem = document.createElement('li');
todoItem.classList.add('todo-list-item');
const label = document.createElement('label');
const check = document.createElement('input');
check.type = 'checkbox';
const close = document.createElement('button');
close.classList.add('delete');
close.tabIndex = 0;
close.textContent = '✘';
close.setAttribute('aria-label', 'close');
label.appendChild(check);
label.appendChild(document.createTextNode(` ${inputEl.value}`));
todoItem.appendChild(label);
todoItem.appendChild(close);
todoListEl.appendChild(todoItem);
inputEl.value = '';
}
});
clearEl.addEventListener('click', event => {
const toRemove = [];
for (let todoItem of Array.from(todoListEl.children)) {
let checkbox = todoItem.querySelector('input[type="checkbox"]')! as HTMLInputElement;
if (checkbox.checked) {
todoListEl.removeChild(todoItem);
}
}
});
Or you could use simpledom:
import * as dom from "@srhazi/simpledom";
const todoListEl = dom.one(HTMLElement, '#todo-list');
const formEl = dom.one(HTMLFormElement, '#todo-add');
const inputEl = dom.oneFrom(formEl, HTMLInputElement, 'input[name="item"]');
dom.on(todoListEl, 'click', Element, '.delete', (e, target) => {
for (let itemEl of Array.from(todoListEl.children)) {
if (itemEl.contains(target)) {
todoListEl.removeChild(itemEl);
}
}
});
formEl.addEventListener('submit', e => {
e.preventDefault();
if (inputEl.value.trim().length > 0) {
todoListEl.appendChild(dom.build(`
<li class="todo-list-item">
<label>
<input type="checkbox"> ${inputEl.value}
</label>
<button class="delete" aria-label="close">✘</button>
</li>
`));
inputEl.value = '';
}
});
dom.on(formEl, 'click', Element, 'button[name="clear"]', () => {
for (let todoItem of Array.from(todoListEl.children)) {
const checkbox = dom.oneFrom(todoItem, HTMLInputElement, 'input[type="checkbox"]');
if (checkbox.checked) {
todoListEl.removeChild(todoItem);
}
}
});