Altering the DOM with JavaScript
If you’re learning JavaScript, the first thing you should learn (after understanding the basics like variables, functions, etc.) is to alter the DOM. This is one of the things you do daily as a frontend developer.
Changing the DOM used to be difficult. We needed jQuery to make things easier. Luckily, there’s no need for jQuery anymore.
In this article, I’ll show you the things you need to be familiar with as a frontend developer.
What do you do with the DOM?
When you work with the DOM, you’ll find yourself needing to do one or more of the following things:
- Selecting HTML elements
- Adding or removing event listeners
- Adding or removing classes
- Adding, changing or removing attributes
- Adding or removing HTML elements
I’ll explain what each of these things are, why you use them and how to do them in the following sections. Let’s jump into the very first one — selecting HTML elements.
Selecting HTML elements
Knowing how to select HTML elements is the first step before you do anything else with the DOM. Once you’ve selected an element, you’ll be able to add event listeners, change classes, and do other fancy things.
You only need to know two methods to select anything you want — querySelector
and querySelectorAll
.
querySelector
querySelector
helps you select one HTML element. If multiple HTML elements are found with your selection, querySelector
always returns the first element. It looks like this:
document.querySelector(selector)
You can select an element by its id, class or even tag with querySelector
.
Let’s walk through a quick example. Say you have the following HTML:
<div id="the-one">ID</div>
<div class="an-awesome-class">Class</div>
<p>A tag</p>
- To select the element with an id, you prepend the id with a
#
- To select the element with a class, you prepend the class with a
.
- To select the element with a tag, you simply write the tag as your selector.
document.querySelector('#the-one')
// => <div id="id">ID</div>
document.querySelector('.an-awesome-class')
// => <div class="an-awesome-class">Class</div>
document.querySelector('p')
// => <p>A tag</p>
Complicated selections
querySelector
is incredibly powerful. You can perform complicated selections by chaining ids, classes and tags together, like this:
document.querySelector('div#the-one')
Although it’s possible to chain selectors, I recommend you don’t do this because it’s unnecessary most of the time.
Selecting elements within elements
Here’s one thing great about querySelector
. You can instruct it to look for an element within another element, which reduces the time needed to lookup a deep selector.
To do so, you add a space between your classes, ids or tags. Here’s an example:
<div class="container">
<div class="inner-item">Inner item!</div>
</div>
let innerItem = document.querySelector('.container .inner-item')
// => <div class="inner-item">Inner item!</div>
Alternatively, if you’ve already selected an element with querySelector
you can also use that element to perform another querySelector
call:
let container = document.querySelector('.container')
let innerItem = container.querySelector('.inner-item')
// => innerItem is <div class="inner-item">Inner item!</div>
That’s all you need to know about querySelector
.
Now, what if you needed to select more than one element? This is where querySelectorAll
comes in.
querySelectorAll
querySelectorAll
is a method that helps you select multiple elements.
let allELements = document.querySelectorAll(selectors)
selectors
, in this case, has the same syntax as querySelector
. The only exception is that you can perform multiple selections by separating selections with a comma (,
).
<div class="thing">A thing</div>
<div class="thing">A thing</div>
<div class="another-thing">Another thing</div>
let allThings = document.querySelectorAll('.thing, .another-thing')
// => [
// <div class="thing">A thing</div>,
// <div class="thing">A thing</div>,
// <div class="another-thing">Another thing</div>
// ]
Here’s the important part.
querySelectorAll
returns a NodeList (even though it looks like an array).
If you’re only working with modern browsers, you can get individual elements within the Nodelist with a NodeList.forEach
call.
If you’re working with older browsers, you need to convert the NodeList into an Array before looping through it with a forEach call. The easiest way to do so is to use Array.from()
.
// Modern browsers
let allThings = document.querySelectorAll('.thing, .another-thing')
allThings.forEach(el => {
/* do something with element */
})
// Older browsers
let allThings = document.querySelectorAll('.thing, .another-thing')
// You might need a polyfill for Array.from.
// Alternatively, use Array.prototype.slice.call(allThings);
let allThingsArray = Array.from(allThings)
allThingsArray.forEach(el => {
/* do something with element */
})
Next, let’s move on to adding and removing event listeners.
Adding and removing event listeners
Event listeners allow your JavaScript to perform an action whenever an event is triggered. This is how you know when a user has interacted with the DOM. One example is when they clicked a button:
See the Pen Altering DOM with JS demoby Zell Liew (@zellwk) onCodePen.
Here, you only need to know two methods — addEventListener
and removeEventListener
.
Adding event listeners
To add your event listener, you first select your HTML element, then call the addEventListener
method. It accepts two parameters, like this:
let thing = document.querySelector('.thing')
thing.addEventListener(event, callback)
event
is the name of the event you want to listen to. These events are already predetermined in the spec. Here’s a handy list of common event types you’ll want.
callback
is a function that does what you want whenever the event is triggered. It contains one parameter — the event object. Here’s what a typical callback looks like:
thing.addEventListener('click', callback)
function callback(e) {
e.preventDefault() // Prevents default behavior. Only use this when necessary
console.log('thing is clicked!')
}
We can do a lot of stuff with the event object (e
), but that’s a topic for another day. Let’s move on to removing event listeners.
Removing Event Listeners
Removing an event listener is similar to adding an event listener. Here, you call the removeEventListener
method, then pass in two parameters — the event
type and your callback.
thing.removeEventListener('click', callback)
Typically, you will only remove an eventListener after a task is completed. So, it’s common to find removeEventListener
within an addEventListener
call.
thing.addEventListener('click', callback)
function callback() {
console.log('thing is clicked!')
// removes event listener
thing.removeEventListener('click', callback)
}
When you remove an event listener, you no longer listen to the event. So, in the code above, the callback only triggers when .thing
gets clicked for the first time. Further clicks on .thing
will not trigger the callback.
Note: you should remove an event listener when you have no more need for it. By doing so, you free up resources for other tasks.
Let’s move on.
Adding and removing classes
Remember button demo above?
See the Pen Altering DOM with JS demoby Zell Liew (@zellwk) onCodePen.
Here’s what I did to make this demo work:
- Add
.is-open
to<nav>
when a user clicks on the button - Remove
.is-open
from<nav>
if<nav>
is already open when the user clicks on the button. - Transitioning the
<nav>
is done with CSS.
This demo shows you the power of CSS when combined with JavaScript. You can create all sorts of interactions just by adding (or removing) a class.
Here’s how you can add a class, remove a class or check if a class exists:
- To add a class, use
element.classList.add('classname')
- To remove a class, use
element.classList.remove('classname')
- To check if a class exists, use
element.classList.contains('classname')
Here’s the code to add or remove .is-open
from <nav>
when you click on the button.
let button = document.querySelector('button')
let nav = document.querySelector('nav')
button.addEventListener('click', toggleNav)
function toggleNav() {
// Checks if nav has is-open class
if (nav.classList.contains('is-open')) {
// removes is-open class
nav.classList.remove('is-open')
} else {
// adds is-open class
nav.classList.add('is-open')
}
}
Let’s move on to adding, changing and removing attributes
Adding, changing and removing attributes
Attributes are an important part of HTML elements. Sometimes, you need to extract information from these attributes to give context to your JavaScript. Other times, you can use these attributes to help write accessible interfaces.
Here’s a demo of the above nav, written in an accessible way:
See the Pen Altering DOM with JS demo (Accessible way)by Zell Liew (@zellwk) onCodePen.
In this demo, two things changed:
- I added
aria-expanded
tobutton
to tell screen readers if the menu is expanded. - I added
aria-hidden
tonav
to prevent screen readers from reading the menu when it’s hidden.
Here’s how you can extract information from and attribute, set an attribute and remove an attribute:
- To get an attribute, use
getAttribute('attribute-name')
- To change/set an attribute, use
setAttribute('attribute-name', 'attribute-value')
- To remove an attribute, use
removeAttribute('attribute-name')
// Get attribute
button.getAttribute('aria-expanded')
// Set attribute
button.setAttribute('aria-expanded', true)
// Remove attribute
button.removeAttribute('aria-expanded')
Finally, let’s move on to adding or removing elements.
Adding or removing elements
Let’s start this section with a demo:
See the Pen Altering DOM with JS demo (Adding and removing elements)by Zell Liew (@zellwk) onCodePen.
If you clicked on the prepend or append button above, you’d see I’ve added the text Hello again, world!
into the DOM as another list item.
Adding elements to the DOM
There are three steps to adding this text into the DOM. They are:
- Create an HTML element with
document.createElement
- Add content to the HTML element by setting the
innerHTML
. - Add it to the DOM with
parentNode.prepend
orparentNode.append
.
let ul = document.querySelector('ul')
// Creating a <li> element
let li = document.createElement('li')
// Adding content to the <li> element
li.innerHTML = 'Hello again, world!'
// Adding it to the DOM
ul.append(li)
Removing elements from the DOM
To remove an element from the DOM, you need to call parentNode.removeChild
. This method takes in a parameter — the element to remove.
ul.removeChild(li)
We can’t simply say remove <li>
and expect the JavaScript to know which list item to remove. We need to tell our JavaScript which one to remove explicitly.
If you can use querySelector
to choose with element to remove, that’s going to be the easiest method:
let parent = document.querySelector('.parent')
let elToRemove = document.querySelector('.element-to-remove')
parent.removeChild(elToRemove)
// Of if you don't want to write a separate querySelector
elToRemove.parentNode.removeChild(elToRemove)
In the demo above, we can’t do this because there’s no way to tell which is the first or last list item with classes or ids.
Instead, we can use parentNode.children
to get a NodeList of elements within ul
, then, use Array methods to get the specific element to remove.
Here’s the code to remove the first child element:
let list = document.querySelector('ul')
removeFirst.addEventListener('click', e => {
if (list.children.length) {
let firstNode = list.children[0]
list.removeChild(firstNode)
}
})
Wrapping up
Altering the DOM is one of the most important things you need to know as a frontend developer. You’ll be able to do all sorts of fancy stuff the moment you learn to work with the DOM.
In this article, I’ve showed you five common ways you need to alter the DOM, plus the relevant code you need to know. Now, go and play with the DOM and create some magic 😎.
What do you think of this article? I’d love to hear your thoughts in the comments below :)