Back to Notes

JavaScript DOM Notes

Document

The DOM Tree & Navigation

Every HTML tag is an object. Nested tags are "children" of the enclosing tag.

  • Nodes vs. Elements:

    • Nodes: Include everything (Comments, Text, Elements). Accessed via childNodes, firstChild, nextSibling.
    • Elements: HTML tags only (ignores text/comments). Accessed via children, firstElementChild, nextElementSibling.
      • children – only those children that are element nodes.
      • firstElementChildlastElementChild – first and last element children.
      • previousElementSiblingnextElementSibling – neighbor elements.
      • parentElement – parent element.
    • The <table> element supports (in addition to the given above) these properties:
      • table.rows – the collection of <tr> elements of the table.
      • table.caption/tHead/tFoot – references to elements <caption><thead><tfoot>.
      • table.tBodies – the collection of <tbody> elements (can be many according to the standard, but there will always be at least one – even if it is not in the source HTML, the browser will put it in the DOM).
      • <thead><tfoot><tbody> elements provide the rows property:
        • tbody.rows – the collection of <tr> inside.
      • <tr>:
        • tr.cells – the collection of <td> and <th> cells inside the given <tr>.
        • tr.sectionRowIndex – the position (index) of the given <tr> inside the enclosing <thead>/<tbody>/<tfoot>.
        • tr.rowIndex – the number of the <tr> in the table as a whole (including all table rows).
      • <td> and <th>:
        • td.cellIndex – the number of the cell inside the enclosing <tr>.
  • Senior Tip: In interviews, almost always use Element navigation (children) to avoid dealing with accidental whitespace text nodes.

Searching the DOM

MethodSearches by...Can call on an element?Live?
querySelectorCSS-selector-
querySelectorAllCSS-selector-
getElementByIdid--
getElementsByNamename-
getElementsByTagNametag or '*'
getElementsByClassNameclass
  • querySelector(css) / **querySelectorAll(css)****:
    • Uses CSS selectors (e.g., .class, #id, div > span).
    • Returns a Static Collection (NodeList). It does not update if the DOM changes later.
  • getElementsBy* (TagName, ClassName, Name):
    • Returns a Live Collection (HTMLCollection).
    • Crucial: If you add a <div> to the DOM after calling getElementsByTagName('div'), the collection automatically grows to include it.
  • By far the most used are querySelector and querySelectorAll, but getElement(s)By* can be sporadically helpful or found in the old scripts. Besides that:
  • There is elem.matches(css) to check if elem matches the given CSS selector.
  • There is elem.closest(css) to look for the nearest ancestor that matches the given CSS-selector. The elem itself is also checked.
    • Ancestors of an element are: parent, the parent of parent, its parent and so on. The ancestors together form the chain of parents from the element to the top. And let’s mention one more method here to check for the child-parent relationship, as it’s sometimes useful:
  • elemA.contains(elemB) returns true if elemB is inside elemA (a descendant of elemA) or when elemA==elemB.

Attributes vs. Properties

  • Attribute: Written in HTML (string). Accessed via elem.getAttribute('class').
  • Property: The DOM object field. Accessed via elem.className.
  • Syncing: Standard properties (id, href) sync. Non-standard ones do not.
    • Gotcha: input.getAttribute('value') gives the initial HTML value. input.value gives the current user input.
  • Each DOM node belongs to a certain class. The classes form a hierarchy. The full set of properties and methods come as the result of inheritance. ![[svgviewer-output.svg]]
  • Main DOM node properties are:
    • nodeType : We can use it to see if a node is a text or an element node. It has a numeric value: 1 for elements,3 for text nodes, and a few others for other node types. Read-only.
    • nodeName/tagName : For elements, tag name (uppercased unless XML-mode). For non-element nodes nodeName describes what it is. Read-only.
    • innerHTML: The HTML content of the element. Can be modified.
    • outerHTML : The full HTML of the element. A write operation into elem.outerHTML does not touch elem itself. Instead it gets replaced with the new HTML in the outer context.
    • nodeValue/data : The content of a non-element node (text, comment). These two are almost the same, usually we use data. Can be modified.
    • textContent : The text inside the element: HTML minus all <tags>. Writing into it puts the text inside the element, with all special characters and tags treated exactly as text. Can safely insert user-generated text and protect from unwanted HTML insertions.
    • hidden : When set to true, does the same as CSS display:none.
    • DOM nodes also have other properties depending on their class. For instance, <input> elements (HTMLInputElement) support valuetype, while <a> elements (HTMLAnchorElement) support href etc. Most standard HTML attributes have a corresponding DOM property.

[!INFO]

  • console.log(elem) shows the element DOM tree.
  • console.dir(elem) shows the element as a DOM object, good to explore its properties.

Modifying the DOM

  • Creation: document.createElement('div').
  • Insertion (Modern):
    • node.append(...nodes) (at end).
    • node.prepend(...nodes) (at start).
    • node.before() / node.after() (siblings).
  • Removal: node.remove().
  • Cloning: node.cloneNode(deep) (true = deep, false = shallow).

Classes and Styles

  • className: The string of all classes (space-separated). Replaces everything.
  • classList: The modern way. Methods: .add(), .remove(), .toggle(), .contains().
  • style: Applies inline styles (elem.style.width = "100px").

Events

Event Propagation

Events travel in 3 phases:

  1. Capture (top → target): event travels down the DOM tree
  2. Target: fires on the element itself
  3. Bubble (target → top): event bubbles back up
// bubbling (default) — handler fires on the way up
elem.addEventListener('click', handler);

// capturing — handler fires on the way down
elem.addEventListener('click', handler, { capture: true });

stopPropagation() — stops the event from travelling further (up or down). preventDefault() — prevents the browser's default action (e.g. form submit, link follow). Does NOT stop propagation.

Event Delegation

Attach ONE listener to a parent instead of many listeners on children. Uses bubbling.

// Instead of adding listeners to every <li>
document.getElementById('list').addEventListener('click', (e) => {
  if (e.target.tagName === 'LI') {
    console.log('Clicked:', e.target.textContent);
  }
});

Benefits: works for dynamically added children, fewer listeners, better memory.

Common Events

CategoryEvents
Mouseclick, dblclick, mouseenter, mouseleave, mouseover, mouseout, mousemove
Keyboardkeydown, keyup, keypress
Formsubmit, change, input, focus, blur, reset
Windowload, DOMContentLoaded, resize, scroll
Dragdragstart, drag, dragend, drop

mouseenter vs mouseover:

  • mouseenter fires once when cursor enters the element, does NOT bubble
  • mouseover fires when cursor enters element OR any child, DOES bubble

Event Object

elem.addEventListener('click', (e) => {
  e.target          // element that triggered the event
  e.currentTarget   // element the listener is attached to
  e.type            // "click"
  e.clientX/Y       // mouse position relative to viewport
  e.key             // key pressed (for keyboard events)
  e.preventDefault()
  e.stopPropagation()
});