Learn Frontend Part 8: The DOM, Events and Browser APIs - Delegation, Debounce and Throttle

Learn Frontend Part 8: The DOM, Events and Browser APIs - CodingHumans

Frameworks like React hide the DOM most of the time, but every interaction still comes down to DOM nodes and browser events. Knowing how events travel through the page, how delegation works and how to throttle expensive handlers makes you faster at debugging, and these topics are staples of frontend interviews and machine-coding rounds.

This is Part 8 of the Learn Frontend series. We cover the DOM tree, efficient DOM updates, event propagation, delegation, debounce and throttle, and the browser APIs that power modern UIs.

1. The DOM tree and working with it

The Document Object Model is the browser's live tree of objects built from your HTML. JavaScript can read and change it, and the browser re-renders what changes.

  • Select: document.querySelector('.card') and querySelectorAll accept any CSS selector; getElementById is the fastest single lookup.
  • Create and insert: document.createElement, then append, prepend, before, after or replaceWith.
  • Content: textContent sets plain text safely; innerHTML parses HTML and can execute injected markup, so never use it with untrusted input (Part 14).
  • Classes and attributes: el.classList.add/remove/toggle, el.setAttribute, el.dataset.userId.
  • Batch changes: build many nodes in a DocumentFragment and insert once, and avoid reading layout (such as offsetHeight) between writes, which forces extra layout work (Part 9).

2. How events travel: capture, target, bubble

When you click a button inside a card inside a list, the event travels in three phases:

  1. Capturing: from window down through the ancestors towards the target.
  2. Target: at the element that was actually clicked (event.target).
  3. Bubbling: back up through each ancestor to window.
  • addEventListener(type, fn) listens during bubbling by default; pass { capture: true } for the capture phase.
  • event.target is where the event started; event.currentTarget is the element whose listener is running.
  • event.stopPropagation() stops the event travelling further; event.preventDefault() cancels the browser's default action, such as submitting a form or following a link. They are unrelated.
  • Options such as { once: true } and { passive: true } (for scroll and touch listeners) help performance; always remove listeners you no longer need.

3. Event delegation

Instead of attaching a listener to every item in a long or changing list, attach one listener to a common parent and use event.target to work out which item was clicked. This uses less memory and automatically works for items added later.

// One listener for the whole list, even for items added later
const list = document.querySelector('#todo-list');

list.addEventListener('click', (event) => {
  const deleteBtn = event.target.closest('button.delete');
  if (!deleteBtn || !list.contains(deleteBtn)) return;   // click was elsewhere

  const item = deleteBtn.closest('li');
  console.log('Deleting item', item.dataset.id);
  item.remove();
});

closest() walks up from the clicked node to find the nearest matching ancestor, so clicks on an icon inside a button still find the button. React uses delegation internally: it attaches listeners at the root and dispatches synthetic events to your components.

4. Debounce and throttle

Some events fire very often: input, scroll, resize, mousemove. Running expensive work on each one wastes CPU and network.

  • Debounce: wait until the events stop for a set time, then run once. Ideal for search-as-you-type and auto-save.
  • Throttle: run at most once per interval while events keep firing. Ideal for scroll position tracking and resize handlers.
function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);                              // restart the wait
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

function throttle(fn, interval) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= interval) {                     // enough time has passed
      last = now;
      fn.apply(this, args);
    }
  };
}

searchInput.addEventListener('input', debounce(e => search(e.target.value), 300));
window.addEventListener('scroll', throttle(updateProgressBar, 100), { passive: true });

Writing these from scratch is one of the most common frontend interview questions, because it combines closures, timers and this/arguments handling.

5. Browser APIs you should know

  • IntersectionObserver: tells you when an element enters or leaves the viewport, without scroll listeners. Used for lazy loading, infinite scroll and analytics impressions.
  • ResizeObserver and MutationObserver: react to element size changes and DOM changes.
  • Storage: localStorage, sessionStorage and IndexedDB for larger structured data (Part 1 compares them).
  • History API: history.pushState and the popstate event let single-page apps change the URL without reloading, which is how client-side routers work.
  • requestAnimationFrame: schedule visual updates right before the next paint for smooth animations.
  • Clipboard, Geolocation, Notifications and Web Workers for background computation.
// Infinite scroll: load more when the sentinel at the bottom becomes visible
const sentinel = document.querySelector('#load-more-sentinel');

const observer = new IntersectionObserver(async (entries) => {
  if (entries[0].isIntersecting) {
    observer.unobserve(sentinel);       // avoid double loading
    await loadNextPage();
    observer.observe(sentinel);
  }
}, { rootMargin: '200px' });            // start a little before it is visible

observer.observe(sentinel);
Key takeaways
  • The DOM is a live tree; prefer textContent over innerHTML for untrusted data and batch DOM writes.
  • Events capture down, hit the target, then bubble up; target is the origin, currentTarget is the listener's element.
  • stopPropagation stops travel; preventDefault cancels the browser's default action.
  • Event delegation uses one parent listener plus closest() and works for dynamic items.
  • Debounce waits for a pause; throttle limits frequency. IntersectionObserver replaces most scroll listeners.

Practice problems

Build these with plain JavaScript; they are common machine-coding round tasks.

  1. Build a to-do list with add, complete and delete using a single delegated listener.
  2. Implement debounce and throttle from scratch and test them on input and scroll events.
  3. Log the capture and bubble phases for a click on a nested element using three listeners.
  4. Build an image gallery that lazy-loads images with IntersectionObserver.
  5. Build an infinite scroll list that loads 20 more items when you reach the bottom.
  6. Build tabs or an accordion that works with the keyboard as well as the mouse.
  7. Build a simple client-side router using history.pushState and popstate.

Interview questions and FAQ

What is event delegation and why is it useful?

It means handling events for many child elements with one listener on a parent, relying on bubbling and event.target. It saves memory, simplifies code and automatically covers elements added later.

What is the difference between event.target and event.currentTarget?

event.target is the element where the event originated, for example the icon inside a button. event.currentTarget is the element the running listener is attached to.

What is the difference between stopPropagation and preventDefault?

stopPropagation stops the event from moving to other elements in the capture or bubble path. preventDefault stops the browser's built-in action, such as following a link or submitting a form. You can use either, both or neither.

What is the difference between debounce and throttle?

Debounce delays running a function until events stop for a given time, so it runs once after a burst. Throttle runs the function at most once per interval during continuous events.

Why is innerHTML considered risky?

It parses a string as HTML, so if the string contains user input, an attacker can inject elements and event handlers that run script (XSS). Use textContent for text, or sanitise HTML with a trusted library.

Comments

Answered 0/0Correct 0