Skip to content

Instantly share code, notes, and snippets.

@ChrisShank
Last active June 20, 2025 13:42
Show Gist options
  • Save ChrisShank/783e39895bd849a7361350e91d2073c6 to your computer and use it in GitHub Desktop.
Save ChrisShank/783e39895bd849a7361350e91d2073c6 to your computer and use it in GitHub Desktop.
custom HTML elements as domain objects in JS
// We can use custom HTML elements as domain objects to abstract away and compose DOM operations as if they are regular JS objects.
class TodoItem extends HTMLElement {
#completed = document.createElement('input');
#text = document.createElement('input');
#button = document.createElement('button');
constructor() {
super();
this.#completed.type = 'checkbox';
this.#text.part.add('text');
this.#completed.part.add('completed');
this.#button.part.add('delete-button');
const shadow = this.attachShadow({mode: 'open'});
shadow.append(this.#completed, this.#text, this.#button);
shadow.addEventListener('click', (e) => {
if (e.target === this.#button) {
this.delete();
}
})
}
connectedCallback() {
this.completed = this.hasAttribute('completed');
this.text = this.getAttribute('text') || '';
}
get completed() {
return this.#completed.checked;
}
set completed(value) {
this.#completed.checked = value;
}
get text() {
return this.#text.value;
}
set text(value) {
this.#text.value = value;
}
delete() {
this.dispatchEvent(new Event('deleted', {bubbles: true}))
this.remove();
}
}
customElements.define('todo-item', TodoItem);
declare global {
interface HTMLElementTagNameMap {
'todo-item': TodoItem;
}
}
// any example of how to programatically interact with todos
function markAllCompleted() {
document.querySelectorAll('todo-item').forEach(todo => {
todo.completed = true;
});
}
function deleteAllCompleted() {
document.querySelectorAll('todo-item').forEach(todo => {
if (todo.completed) todo.delete();
});
}
@ChrisShank
Copy link
Author

Here is an older, but more full-fledged example of this approach of a trello board.

https://github.com/ChrisShank/vrello/blob/main/client/main.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment