DOM events
| addEventListener | |
| Typed event targets | |
| Delegate lists | |
| Once and cleanup |
addEventListener
addEventListener attaches a reaction to an element without touching its markup. The callback receives an event object, and TypeScript picks the right event type from the event name, so a click handler sees a MouseEvent with its coordinates:
const btn = document.querySelector<HTMLButtonElement>("button");
if (btn === null) {
throw new Error("missing button");
}
btn.addEventListener("click", (event) => {
console.log(event.clientX, event.clientY);
});
The event parameter needs no annotation: the click name tells the compiler its exact type. clientX and clientY are therefore checked, not guessed.
Typed event targets
Using btn from the previous section, narrow the event target before touching it. The target arrives as a wide type, and instanceof shrinks it to the concrete element, unlocking typed fields like disabled with no casts at all:
btn.addEventListener("click", (event) => {
const target = event.target;
if (target instanceof HTMLButtonElement) {
target.disabled = true;
}
});
The check does the work that a cast would fake. Inside the if, target is a button with every button field available.
Delegate lists
One listener on the parent handles every child. Clicks bubble up, so read the actual clicked item from the event target and match it with closest. Rows added later work at once, with no extra listeners:
const list = document.querySelector<HTMLUListElement>("ul");
if (list === null) {
throw new Error("missing ul");
}
list.addEventListener("click", (event) => {
const target = event.target;
if (!(target instanceof HTMLElement)) {
return;
}
const item = target.closest("li");
if (item !== null) {
item.classList.toggle("done");
}
});
closest walks from the clicked node up to the nearest li, or returns null outside any row. The toggle then flips the done class exactly where the user clicked.
Once and cleanup
Listeners pile up when added twice, so remove what is no longer needed. Pass once for handlers that must fire a single time. For manual control, keep the function reference and call removeEventListener later with the same reference:
function welcome(): void {
console.log("welcome");
}
btn.addEventListener("click", welcome, { once: true });
once removes the listener after the first click automatically. Anonymous arrows cannot be removed by reference, which is one more reason to name handlers like welcome.
Next: Fetch in browsers
Article author: Arthur Isaev