Document Object Model (DOM)

·

9 min read

The Document Object Model (DOM) is a structured representation of an HTML document. It organizes the document into a tree of objects, making it possible to manipulate and interact with elements programmatically.

document         // returns the entire document
document.all   // returns the entire document in an HTML collection format
document.all[i]   // returns the element at the ith index. Html is 0th index
document.all.length   // returns the length
document.head   // returns the head element
document.body   // returns the body element
document.doctype  // returns the doctype
document.domain   // returns the domain
document.URL   // returns the URL (including the domain)
document.characterSet   // returns the characterSet

document.forms   // returns an HTML collection all forms in our document
document.forms[i]   // returns the specific form at the ith index
document.forms[i].id  // returns the id of that specific form
document.forms[i].method  // returns the method of that specific form
document.forms[i].action  // returns the action of that specific form

document.links   // returns an HTML collection all links in our document
document.links[i]   // returns the specific link at the ith index.
document.links[i].id  // returns the id of that specific link
document.links[i].className  // returns string of all classes in that link
document.links[i].classList  //returns DOMTokenList collection of all classes 
document.links[i].classList[n]  //returns a specific class name at that index

// we can do the same with images, scripts et cetra
document.images  //  returns an HTML collection all images in our document
document.scripts   //  returns an HTML collection all scripts in our document
document.scripts[i].getAtrribute('src')  // returns the src attribute 

const scripts = Array.from(document.scripts)

Note: An HTMLCollection resembles an array but is not a true array. To use methods like forEach, you must first convert it to an array, typically using Array.from() or the spread operator ([...]).

We have two types of selectors in JavaScript: single-element selectors, which select a single element from the DOM, and multiple-element selectors, which select a collection of elements. Single-element selectors only target the first matching element if there is more than one of the same type. The two most commonly used single-element selectors are getElementById() and querySelector(). The latter is more versatile because it can accept any valid CSS selector, while the former is limited to selecting elements by their id attribute.

To demonstrate this add the following code in the body of your HTML file.

<body>
    <div id="item" class="items">
        This is some <span style="display: none">hidden</span> text.
    </div>
    <h5>Study List</h5>
    <ul class="lists">
      <!--this is comment-->
      <li class="list">HTML</li>
      <li class="list">CSS</li>
      <li class="list">JS</li>
    </ul>
    <script src="index.js"></script>
</body>
// Select element by Id
const item = document.getElementById('item') //returns the element of that id

// accessing the attributes of the item selected
item.id // returns item
item.className  // returns a string of all its classes; "items" in this case
item.textContent // returns This is some hidden text.
item.innerText // returns This is some text.
item.innerHTML // This is some <span style="display: none">hidden</span> text.

// changing the values of the attributes of the item selected
item.style.background = '#000' // we can change its style
item.innerText = 'New content' // we can change its content

// we can add/remove class
item.classList.add('class-name')
item.classList.remove('class-name')

// get/set/remove/has attribute
item.setAttribute('title', 'Title') // first argument attribute second value 
item.getAttribute('title') // returns value of the attribute
item.removeAttribute('title') // removes an attribute
item.hasAttribute('title') // checks if the element has that attribute  

// different selectors
document.querySelector('#item')  // returns the element with the id: item
document.querySelector('.lists')  // returns the first element of class: lists
document.querySelector('h5')  // returns the first h5 element
document.querySelector('li:last-child')  // returns the last li element
document.querySelector('li:nth-child(2)')  // returns the 2nd li element

Multiple-element selectors return a collection of elements, which may either be an HTMLCollection or a NodeList. The more modern querySelectorAll() method is the most common way to create a NodeList of elements. While neither an HTMLCollection nor a NodeList is a true array, you can iterate over a NodeList using methods like forEach.

// HTMLCollection of all elements by class name 
document.getElementsByClassName('item')  // returns all with class name: item

// HTMLCollection of all elements by tag name
document.getElementsByTagName('li')  // returns all elements with the tag: li  

// Query selector all (node list)
document.querySelectorAll('li:nth-child(odd)')  // returns all odd li elements

When working with multiple elements, traversing the DOM is often necessary to capture the elements we want to manipulate. For this purpose, JavaScript provides built-in traversing techniques that can target specific nodes. Some methods work with multiple elements, while others are designed for single elements.

A node list may contain not only HTML elements but also text nodes— which can be explicitly written or generated from a line break— and comments within the parent element. These nodes have properties such as nodeName and nodeType. Some of these are demonstrated in the table below.

NodeNode nameNode type
<h5>Study List</h5>h51
Study List#text3
<!--this is comment-->#comment8
const list = document.querySelector('.lists')

// get child nodes
list.childNodes // returns node list of all children includes 
list.childNodes[0].nodeName  // returns the node name of the first item 
list.childNodes[0].nodeType  // returns the node type of the first item 

// get children elements (excludes comment or text content)
list.children   // returns HTMLCollection of all children of the element

// get first child
list.firstChild  // returns the first node => text node if there's line break
list.firstElementChild // returns the first element

// get last child
list.lastChild  // returns the last node => text node if there's line break
list.lastElementChild  // returns the last element

// get parent element 
list.parentNode // returns the parent node
list.parentElement // returns parent element which is almost always the node

// get siblings 
list.previousSibling // returns previous node=> text node if there's line break
list.previousElementSibling  // returns the previous element
list.nextSibling  // returns next node => text node if there's line break
list.nextElementSibling  // returns the next element

We can create a brand-new element, such as a list item, using the syntax document.createElement('li'). Once created, we can assign it a class, an ID, an attribute, or even a child element! However, after creating the element, we need to append it to an existing HTML element; otherwise, it will remain unused in the DOM. Similarly, we can also remove or replace existing HTML elements.

// Creating an element with some properties
const newLi = document.createElement('li') // create a new list element
newLi.className = 'item'  // Add class to it
newLi.id = 'item'  // Add id to it
newLi.setAttribute('title', 'New Item')  // Add attribute to it
newLi.appendChild(document.createTextNode('Hello')) // Add text node to it

// Appending the element just created to an existing HTML element (parent)
document.querySelector('.items').appendChild(newLi) //Append li (child) to ul 

// Replacing an element
const old = document.querySelector('.item');     // Select the old li
const parent = document.querySelector('.items');   // Select the parent (ul) 
parent.replaceChild(newLi, old);   // Replace the old li with the new li

// Removing an element
document.querySelector('.item').remove() // removes an element   
document.querySelector('.items').removeChild(old) //remove child from a parent

Note: The append() method works similarly to appendChild() but has additional flexibility. It can accept multiple arguments—which can be elements or even nodes like strings— like this: parent.append(child1, child2). The arguments are appended in the order they are listed. However, keep in mind that append() is not supported in older browsers.

We can listen to events on almost anything in the DOM. To achieve this, we use the addEventListener() method, which takes two arguments: the event we want to listen to (for example, a button click event) and a callback function (anonymous, named, or arrow) that contains the code to execute when the event is triggered.

There are many events that we can listen to, some of which are listed in the table below. Even among these, there are different types of events. For a comprehensive explanation of event categories with examples, visit the event section of the Mozilla Developer Network documentation. If you're looking for just a straightforward list of events, you can check out this link.

Event fromMDN referenceExample
MouseMouseEventclick mouseover
KeyboardKeyboardEventkeypress keydown keyup
InputInputEventinput
FormHTMLFormElementsubmit

Once we capture an event fired on a target element, we can perform various actions on that target element, its parent, or its child elements. The following JavaScript code snippet demonstrates some actions performed on the target element. Before running the script, add a button to your HTML file just above the <script> tag like this:

  <body>
    <!-- previous code -->
    <button>Add</button>  <!-- Add a button -->
    <script src="index.js"></script>
  </body>let button = document.querySelector('button')
// target the button element
let button = document.querySelector('button') 
// listen to a click event on that button and call the callback function
button.addEventListener('click', callback)
// callback function
function callback() {
  console.log("I'm clicked") // prints I'm clicked when called
}

Note: If the element we chose was an anchor (<a>) instead of a button, with an href attribute set to anything other than #, we would need to prevent its default behavior—which refreshes the browser—by calling preventDefault() on the event object. This ensures the "I’m clicked" message is displayed on the console.

function callback(event) {
  event.preventDefault() // MUST BE the first line in this function
  console.log("I'm clicked") // prints I'm clicked when called
}

The event object has numerous properties that we can access to perform various operations. Some of the most commonly used ones are demonstrated in the code snippet below.

function callback(event) {
  event.type // returns "click"
  event.target // returns "button"
  event.pointerType // "returns mouse"
  event.offsetX // x-axis of the mouse pointer relative to the target element 
}

The two ways our browser provides for data persistence are local storage and session storage. The primary difference between the two is that local storage retains data until it is explicitly erased, while session storage keeps data only as long as the browser remains open.

Data is stored in JSON string format, which is a structure of key-value pairs. To store something like an array, we need to convert it into this format using the JSON.stringify() method. When retrieving the data, we can convert it back into an array (or its original structure) using the JSON.parse() method.

// setting/saving items on local/session storage
localStorage.setItem('name', 'Khalid') // lives until we erase it
sessionStorage.setItem('name', 'Khalid') // lives until we close the browser

// save an array of values
let fruits = ["apple", "banana", "orange"]
localStorage.setItem('fruits', JSON.stringify(fruits))

// accessing saved item
localStorage.getItem('name')
fruits = JSON.parse(localStorage.getItem('fruits'))

// to erase an item from the local storage
localStorage.removeItem('name')

// to erase everything from the local storage
localStorage.clear()

Credits and helpful resources:

Documentation: MDN Web Docs

Video resources: Traversy Media