All about DOM API and Browser Performance
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
documentnode: The root of all nodesElement 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: theelement.stylecan return the inline CSS style, likeelement.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 endelement.prepend(node): Adds a child element at the beginningelement.before(node): Inserts a new node before an elementelement.after(node): Inserts a new node after an elementelement.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 | element.innerHTML = "<p>Hello World</p>" |
Removing
element.remove()1
2const element = document.querySelector("#box");
element.remove(); // deletes itself from DOMelement.removeChild(child)1
2
3const parent = document.querySelector("#container");
const child = document.querySelector("#box");
parent.removeChild(child);element.replaceChild(newElement, oldElement)1
2
3
4const 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
documentobject, 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.targetreturns the element that triggers the event.event.currentTargetreturns 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
3element.addEventListener("click", (event) => {
event.stopPropagation();
})event.stopImmediatePropagation()Note that this not only stops propagation, but also prevents other event listeners on the target.1
2
3element.addEventListener('click', (event) => {
event.stopImmediatePropagation();
});
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 | document.getElementById('list').addEventListener('click', (e) => { |
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.targetRemember that
event.targetpoints to the most specific element in the DOM tree that triggers the event.
Traversing the DOM
Going downwards: child
querySelectorAll()andquerySelectorchildNodes: 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 nodesfirstElementChildandlastElementChild: get the first/last element within the target.
Going upwards: parent
parentNode: returns the parent node, sometimes might bedocumentparentElement: 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 commentspreviousElementSibling/nextElementSibling: get the previous/next element node.
Bubbling and
closestWhen using event deligation, the
event.targetmay not always work as we want it to be:Like when the user clicks a
<div class="list-item>, there might be aspaninside it. If the user click thespan, the result ofevent.targetwould bespaninstead of thediv.If we still want to get the
div, we could use:
1 event.target.closest(".list-item");

