Notes on DOM manipulation

Elements on a website can be controlled and manipulated with JavaScript. This post looks at how this can be achieved.

a bored fish

Introduction to DOM manipulation

I was brushing up my knowledge of the document object model and discovered that Wikipedia had a more understandable description of it than MDN docs. It describes the DOM as a ‘cross-platform and language-independent interface that treats an HTML or XML document as a tree structure wherein each node is an object representing a part of the document’.1

Let’s take a look at some basic HTML and see what the tree structure looks like.

<!DOCTYPE html>
<html lang="en">
<head>
 <title>Document</title>
</head>
<body>
 <nav>
  <ul>
   <li>
    <a href="/">Home</a>
   </li>
  </ul>
 </nav>
 <header>
  <h1>Main title</h1>
 </header>
 <main>
  <section>
   <h2>Section title</h2>
   <p>First paragraph</p>
  </section>
  <aside>
   <a href="">Link 1</a>
   <a href="">Link 2</a>
  </aside>
 </main>
</body>
</html>

The tree structure

The DOM tree structure of the HTML above is represented below as a bulleted list. The root node is the document. It has many child nodes that represent HTML elements, attributes (not shown), or text content.

All nodes apart from the root have parent nodes. Some have parent of parent nodes! 😳

The tree is formed by the browser when a web page is loaded and allows JavaScript to interact with the object. JavaScript can now be used to change, add or remove nodes; change any associated styling by CSS; and create and remove events such as clicks or inputs. To do so JavaScript needs to target the HTML elements and this is achieved by identifying either the HTML tag, an id attribute, a class attribute, or a combination of these.

class and id attributes

Class and id attributes provide the ability to target HTML element(s) with CSS and Javascript. It is good practise to use id if there is a single use-case in the HTML document. If there are multiple use-cases the class attribute must be used.

For example the code below reflects a single aside tag in a document and therefore it has been given an id attribute. Because there is more than one anchor tag in the aside a class attribute has been added to each. For new developers neither id or class attributes are required if styling of JavaScript ‘targeting’ is not being used.

<aside id="side-panel">
 <a href="here.html" class="aside-link">Link 1</a>
 <a href="there.html" class="aside-link">Link 2</a>
</aside>

CSS with class and id attributes

The associated CSS selectors for id and class are # and . respectively.

#side-panel {
  background-color: '#e5e5e5';
}
.side-link {
  color: green;
}

JavaScript with id attributes

Let’s now take a look at hooking up the JavaScript.

.getElementById() method

Given an HTML element with a unique id attribute, we can use the document’s .getElementById() method to access it. For example, using the HTML element above with the id side-panel:

const theAside = document.getElementById('side-panel');

.querySelector() method

Alternatively, the querySelector() method could be used to access the same id. But notice that the # and id-name syntax is passed into the method.

const theAside = document.querySelector('#side-panel');

Both of these methods do the same job and it boils down to developer preference which is used. Personally, I favour .getElementById() because it targets a known singular id. A 2017 blog post entitled, JavaScript Selector Performance 2 reported .getElementById() performs ‘more than twice as fast’ than the querySelector() method.

JavaScript with class attributes

Note: Remember that the class attribute is normally used in multi-use cases like those pesky li elements with a class aside-link.

.querySelector() method

The querySelector() method could be used to access the same class. But notice that the . and id-name syntax needs to be passed into the method.

const theAsideLinks = document.querySelector('.aside-link');

LEARNING POINT: This variable does not return all the elements with the class aside-link. It only returns the first element found with the class! 👀

.querySelectorAll() method

To access all the elements with a given class the .querySelectorAll() is used. It returns a static array-like node list, which can be manipulated accordingly.

Learn more about Arrays in JavaScript 3 and Array methods in JavaScript 4

DOM element manipulation

Now we know the JavaScript syntax for targeting the DOM elements we can manipluate the text content, styling, attributes, inner HTML etcetera.

Changing styles with JavaScript

Here is a simple HTML div element with a paragraph tag.

<div id='cell'>
  <p class='para'>Hello world</p>
</div>

It is intially styled with CSS.

#cell {
  background-color: 'lightgray';
}
.para {
  color: 'darkgray';
}

The styles can be dynamically changed with JavaScript.

document.getElementById('side-panel').style.backgroundColor = 'gold';
document.querySelector('.para').style.color = 'black';

LEARNING POINT: JavaScript uses camelCase therefore the CSS background-color attribute is written backgroundColor in JavaScript.

Changing text content with JavaScript

<button id="logInOut">Log in</button>

Notice here that we can use a conditional statement for changing the text.

let userLoggedIn = false;

if (userLoggedIn) {
 document.getElementById('logInOut').textContent = 'Log out';
}

In essence, if the user is logged in the button text reads ‘Log out’ and vice versa.

Changing inner HTML with JavaScript

Additional HTML can be injected too using the inbuilt innerHTML property instead of textContent.

<button id="logInOut">Log in</button>
<p class="greeting"></p>
let userLoggedIn = false;
let userName = 'Naomi';

if (userLoggedIn) {
 document.getElementById('logInOut').innerHTML = `<em>Log out</em>`;
 document.querySelector('.greeting').innerHTML = `Welcome <strong>${userName}</strong>`;
}

Add or remove attributes with JavaScript

In this example the classList property is used to add and remove a class from an HTML element.

<button id="subscribeButton">Subscribe</button>
.hide {
  display: none;
}

The CSS class hide is dynamically added or removed depending on whether the user has subscribed, for example to a newsletter.

let userSubscribed = false;

if (userSubscribed) {
  document.getElementById('subscribeButton').classList.add('hide')
} else {
  document.getElementById('subscribeButton').classList.remove('hide')
}

Changes to multiple elements

Remember we used class attributes on multiple elements and targeted the JavaScript with .querySelectorAll()? Just like manipulating single elements, we can do the same with multiple ones.

<p>First paragraph</p>
<p>Second paragraph</p>
<p>Third paragraph</p>

The .querySelectorAll() returns an array-like nodeList, which can be looped, or iterated over, using forEach(). The code below loops over the three paragraph elements and colors the text red.

const para = document.querySelectorAll('p');
para.forEach((item) => item.style.color = 'red' );

If you want to know more I have written a post about Loops in JavaScript

Tidying up the JavaScript

Typing document.getElementById(”), document.querySelector(”) and document.querySelectorAll(”) can get tedious and the resulting code is difficult to read. We can tidy things up a bit with JavaScript variables. You should help yourself by naming the variables with a descriptive name to keep the code readable.

const logInBtn = document.getElementById('logInOut');
const subscribeBtn = document.getElementById('subscribeButton');

Subsequent code becomes shorter and more readable.

if (userLoggedIn) {
 logInBtn.innerHTML = `<em>Log out</em>`;
}

if (userSubscribed) {
 subscribeBtn.classList.add('hide')
} else {
 subscribeBtn.classList.remove('hide')
}

I read somewhere that you should not have variables in the global scope, so I am now in the habit of storing them in an object instead, usually named ui for user interface. Here is such syntax:

const ui = {
 logInBtn: document.getElementById('logInOut');
 subscribeBtn: document.getElementById('subscribeButton');
}

Now the subsequent code is also short and readable.

if (userLoggedIn) {
 ui.logInBtn.innerHTML = `<em>Log out</em>`;
}

if (userSubscribed) {
 ui.subscribeBtn.classList.add('hide')
} else {
 ui.subscribeBtn.classList.remove('hide')
}

Read more information about object syntax and useage on my Objects in JavaScript page

Summary of DOM manipulation

We learned that the Document Object Model (DOM) creates a tree structure of nodes from our HTML file. Then discovered how to add id and class attributes to chosen nodes. Targeting those nodes is made possible with the JavaScript .getElementById(), .querySelector() and .querySelectorAll methods. Using these the attributes, styles and content can be manipulated dynamically.

Tadaa! 😎

Footnotes

  1. Document Object Model on Wikipedia.

  2. JavaScript Selector Performance, GoMakeThings

  3. My notes on Arrays in JavaScript

  4. My notes on Array methods in JavaScript

Thank you for stumbling across this website and for reading my blog post entitled Notes on DOM manipulation