On this page

DOM - Selection and modification

15 min read TextCh. 3 — DOM and events

What is the DOM?

The DOM (Document Object Model) is the in-memory representation of an HTML page's structure. JavaScript can read and modify this structure to create interactive interfaces.

The browser converts each HTML tag into a node in the DOM tree. Each node is a JavaScript object with properties and methods.

Selecting elements

The two main methods are:

  • querySelector(selector) — Returns the first element that matches the CSS selector
  • querySelectorAll(selector) — Returns all matching elements as a NodeList
// Valid CSS selectors
document.querySelector('.class');
document.querySelector('#id');
document.querySelector('div > p:first-child');
document.querySelector('[data-role="admin"]');

NodeList vs Array

querySelectorAll returns a NodeList, not an array. It supports forEach, but not map or filter. To use those methods, convert it to an array with the spread operator:

const items = [...document.querySelectorAll('.item')];

DOM navigation

From any element you can navigate to its neighbors:

Property Returns
parentElement Parent element
children Direct children (elements)
firstElementChild First child element
lastElementChild Last child element
nextElementSibling Next sibling
previousElementSibling Previous sibling

Modifying content

  • textContent — Sets plain text. Safe against XSS
  • innerHTML — Sets HTML content. Dangerous with user data

Modifying CSS classes

The classList property provides methods to manipulate classes:

Method Action
add('class') Adds one or more classes
remove('class') Removes one or more classes
toggle('class') Toggles the class
replace('old', 'new') Replaces a class
contains('class') Checks if it exists (boolean)

Attributes and dataset

For generic attributes use setAttribute / getAttribute. For custom data, data attributes are the standard way:

<div data-user-id="42" data-role="admin">...</div>
const el = document.querySelector('[data-user-id]');
el.dataset.userId;  // '42' (automatic camelCase)
el.dataset.role;    // 'admin'

Creating and removing elements

To add dynamic content, create elements with createElement, configure them, and then insert them into the DOM with appendChild, prepend, or insertBefore.

To remove an element, simply call element.remove().


Practice

  1. Select and modify elements: Use querySelector to select an <h1> and change its textContent. Then select all <li> elements with querySelectorAll, convert them to an array, and filter those that have the class 'active'.
  2. Manipulate classes and attributes: Select an element by its class, use classList.toggle to toggle a 'highlighted' class, and set a custom data-attribute with dataset.
  3. Create elements dynamically: Create a function that receives an array of texts and generates a <ul> with one <li> per text using createElement, textContent, and appendChild. Insert it into the DOM.

In the next lesson we will learn about events and the delegation pattern.

Avoid innerHTML when possible
Using innerHTML with user content can cause XSS (Cross-Site Scripting) attacks. Prefer textContent for plain text and createElement for new elements.
dataset for custom data
Use data attributes (data-id, data-price) to store custom data on HTML elements. Access them with element.dataset.name without the data- prefix.
javascript
// === SELECTING ELEMENTS ===

// querySelector - first matching element
const title = document.querySelector('h1');
const button = document.querySelector('.btn-primary');
const form = document.querySelector('#login-form');

// querySelectorAll - all matching elements (NodeList)
const items = document.querySelectorAll('.menu-item');
const paragraphs = document.querySelectorAll('article p');

// Iterating a NodeList
items.forEach(item => {
  console.log(item.textContent);
});

// Convert NodeList to array (to use map, filter, etc.)
const listArray = [...document.querySelectorAll('li')];
const activeItems = listArray.filter(li =>
  li.classList.contains('active')
);

// Search within an element
const nav = document.querySelector('nav');
const links = nav.querySelectorAll('a');

// Navigate the DOM
const parent = title.parentElement;
const next = title.nextElementSibling;
const children = nav.children; // HTMLCollection
javascript
// === MODIFYING CONTENT ===
const title = document.querySelector('h1');

// textContent - plain text (safe)
title.textContent = 'New title';

// innerHTML - HTML (beware of XSS!)
// title.innerHTML = '<em>Title</em> with HTML';

// === CSS CLASSES ===
const card = document.querySelector('.card');

card.classList.add('active', 'featured');
card.classList.remove('hidden');
card.classList.toggle('expanded');
card.classList.replace('old', 'new');
const isActive = card.classList.contains('active'); // true

// === ATTRIBUTES ===
const link = document.querySelector('a');
link.setAttribute('href', 'https://bemorex.com');
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
const url = link.getAttribute('href');
link.removeAttribute('title');

// Data attributes
const product = document.querySelector('[data-id]');
const id = product.dataset.id;        // data-id
const price = product.dataset.price;   // data-price

// === CREATING AND ADDING ELEMENTS ===
const list = document.querySelector('.list');
const newItem = document.createElement('li');
newItem.textContent = 'New item';
newItem.classList.add('item');

// Different positions
list.appendChild(newItem);                          // at the end
list.prepend(newItem);                              // at the start
list.insertBefore(newItem, list.children[2]);       // before the 3rd

// Remove
newItem.remove();