The DOM API

The Document Object Model (DOM) is a tree-like, in-memory representation of your HTML document.
When the browser parses an HTML file, it creates objects that form a hierarchical tree structure where:

  • Each HTML element becomes a node.

  • Text inside elements becomes text nodes.

  • The relationships between elements (nested tags) form parent-child links.

So the key fact is that in DOM, EVERYTHING is about nodes.


DOM Node

Behind the scenes, each DOM node is a complex object with:

  • Properties (like .nodeName, .textContent, .attributes)

  • Pointers to parent and child nodes

  • Event listeners

  • Metadata (like layout box, dimensions, etc.)

  • The document node: The root of all nodes

  • Element node: when we create a new HTML tag, an element node appears.

  • Text node: the text wrapped inside HTML tags.

Element node

Inside a DOM node, there are several methods that are universal:

Category Property / Method Description
Basic Node Info nodeType Always 1 for Element Nodes
nodeName Tag name of the element (e.g., "DIV", "SPAN")
tagName Same as nodeName (usually uppercase in HTML)
DOM Relationships parentNode Points to the parent node (another Element or Document)
childNodes Includes all child nodes (elements, text, comments)
children Includes only element-type children (no text/comment)
firstChild / lastChild First / last child node
nextSibling / previousSibling Adjacent sibling nodes
Attributes & Properties attributes A NamedNodeMap containing all attributes (e.g., id, class, src)
getAttribute(name) / setAttribute(name, value) Read or modify an HTML attribute
id, className, classList, style Common DOM property interfaces
Content innerHTML HTML string inside the element
outerHTML Full HTML string including the element itself
textContent Plain text content of the element
Element Query querySelector() / querySelectorAll() Search for elements within the current node
Event System addEventListener(type, handler) Register an event listener
DOM Manipulation append(), appendChild(), remove(), replaceWith(), cloneNode() Dynamically modify DOM structure

However, for certain nodes, they may have special attributes that are unique:

For element node that are created using HTML tags, there are properties like:

Property Description Example
tagName The element’s tag name (uppercase) div.tagName → "DIV"
id / className Shortcut access to HTML attributes div.id = "main"
classList A manipulable list of all class names div.classList.add("active")
attributes A NamedNodeMap containing all attributes div.attributes["id"].value
getAttribute() / setAttribute() Read or modify an attribute div.getAttribute("id")
hasAttribute() / removeAttribute() Check for or remove an attribute div.removeAttribute("hidden")
innerHTML Returns the HTML content inside the element div.innerHTML
outerHTML Returns HTML including the element itself div.outerHTML
style Inline style object (CSSStyleDeclaration) div.style.backgroundColor = "red"
dataset Access custom data-* attributes <div data-info="hello"> → div.dataset.info
clientWidth / offsetHeight Element size information div.clientHeight

and since there are many types of HTML elements, some element may have special properties:

Property / Method Description Example
hidden Whether the element is hidden (equivalent to display: none) div.hidden = true
tabIndex Keyboard tab order index button.tabIndex = 1
click() Simulate a mouse click button.click()
focus() / blur() Control focus state input.focus()

style: the element.style can return the inline CSS style, like element.style.color. If the requested style is from external .css file, nothing will be returned.

Therefore, when we are not trying to change the css styles, we often use element.getComputedStyle().

eg. element.getComputedStyle().color

Create, select and delete DOM element

Select

  • document.getElementById()
  • document.getElementByClassName()
  • document.getElementByTag()
  • document.querySelector()
  • document.querySelectorAll()

Create

First we create the node using document.createElement(), then we insert it using:

  • element.appendChild(node): Adds a child element at the end
  • element.prepend(node): Adds a child element at the beginning
  • element.before(node): Inserts a new node before an element
  • element.after(node): Inserts a new node after an element
  • element.insertAdjacentHTML(position, html): Inserts HTML at specific positions (beforebegin, afterbegin, beforeend, afterend)

We can also substitute the HTML elements inside/outside the selected element, but it is not recommended.

1
2
3
4
5
element.innerHTML = "<p>Hello World</p>"
// substitutes everything inside the element

element.outerHTML = "<div></div>"
//substitutes the element itself

Removing

  • element.remove()
    1
    2
    const element = document.querySelector("#box");
    element.remove(); // deletes itself from DOM
  • element.removeChild(child)
    1
    2
    3
    const parent = document.querySelector("#container");
    const child = document.querySelector("#box");
    parent.removeChild(child);
  • element.replaceChild(newElement, oldElement)
    1
    2
    3
    4
    const newEl = document.createElement("div");
    const oldEl = document.querySelector("#old");
    document.body.replaceChild(newEl, oldEl);

Event and Event listeners

Event

An event in JavaScript is a signal that something has happened — usually triggered by user interaction or by the browser itself.

When an event occurs, the browser creates an Event object,
and sends it to any event listeners that are watching for that type of event.

There are many types of events:

Category Common Events Description
️ Mouse Events click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave, contextmenu Fired by mouse actions
Keyboard Events keydown, keypress, keyup Triggered when keys are pressed or released
Form Events submit, change, focus, blur, input, reset Related to form fields
Window / Document Events load, resize, scroll, unload, beforeunload, DOMContentLoaded Fired on global page actions
Clipboard Events copy, cut, paste Interactions with the clipboard
Drag & Drop Events dragstart, drag, dragover, drop, dragend For dragging elements
Touch Events (Mobile) touchstart, touchmove, touchend, touchcancel For touch screens
Focus Events focus, blur, focusin, focusout When elements gain or lose focus
Media Events play, pause, ended, volumechange, timeupdate For <audio> and <video> elements
Animation / Transition animationstart, animationend, transitionend CSS animations and transitions
Network / Resource error, abort, load Image loading and fetch requests
Custom Events Created with new CustomEvent() User-defined events

Event Propagation

Usually when we trigger an event, there are three phase before the event is finished:

  • Capture Phase: the event object is created at the root, which is the document object, and then travels through the DOM tree to reach the target element, passing through all its parent elements.
  • Target Phase: the event reaches the target and triggers event listeners.
  • Bubble Phase: the event travels back to the root, passing all parent elements again.

In the Capture and Bubble phase, while traversing all parent elements, it is like the event is also triggered for each parent node.

A very good way to trace the capture and bubbling is using event.currentTarget.

  • event.target returns the element that triggers the event.
  • event.currentTarget returns the element where the event is currently being handled.

However, when we use event listeners to “listen” to an event, the event usually goes through the bubble phase not the capture phase. That is because by default, event listeners neglect the capture phase.

1
element.addEventListener("click", () => {}, true);

But we can still listen to the capture phase if we manually set the third parameter as true.

Stop Bubbling

Bubbling can be very useful, but sometimes we only want the event to stay at its target. We can manually stop the bubbling using:

  • event.stopPropagation()
    1
    2
    3
    element.addEventListener("click", (event) => {
    event.stopPropagation();
    })
  • event.stopImmediatePropagation()
    1
    2
    3
    element.addEventListener('click', (event) => {
    event.stopImmediatePropagation();
    });
    Note that this not only stops propagation, but also prevents other event listeners on the target.

Event Delegation

Sometimes we want to add event listeners to multiple objects, like every <li> item in a list. If the number of the list items get too big, we may effectively add too many event listeners, which could severely impact the performance.

Using event delegation, we can use one listener to monitor multiple similar events.

1
2
3
4
5
document.getElementById('list').addEventListener('click', (e) => {
if (e.target.tagName === 'LI') {
e.target.classList.toggle('selected');
}
});

So the basic steps are:

  • Attach the event listener to the common parent.
  • Determine which element originated the event.
  • Attach matching strategy.

Why is this possible?

Because when we click on the list items, the click event spreads from the item and bubbles up to its parent, the list.

When the event listener in the list captures the event, we can trace the original target that triggers the event, that is the list item, using event.target

Remember that event.target points to the most specific element in the DOM tree that triggers the event.

Traversing the DOM

  • Going downwards: child

    • querySelectorAll() and querySelector
    • childNodes: get every DOM node within the element, including comments and text nodes.
    • children: get every DOM node that is an element, not including comments and text nodes
    • firstElementChild and lastElementChild: get the first/last element within the target.
  • Going upwards: parent

    • parentNode: returns the parent node, sometimes might be document
    • parentElement: returns the parent element, if the parent is not an element(document) returns null.
    • closest("cssSelector"): get the closest parent element that can be selected using css selector, including the element itself.
  • Going sideways: siblings

    • previousSibling / nextSibling: get the previous/next node, including text nodes and comments
    • previousElementSibling / nextElementSibling: get the previous/next element node.

Bubbling and closest

When using event deligation, the event.target may not always work as we want it to be:

Like when the user clicks a <div class="list-item>, there might be a span inside it. If the user click the span, the result of event.target would be span instead of the div.

If we still want to get the div, we could use:

1
event.target.closest(".list-item");