If you ever wondered how websites created navbars or buttons which “create” content out of nothing, most of the time you are dealing with DOM Manipulation.
DOM stands for Document Object Model and the thing you see in the browser is the DOM itself.
It may look like the DOM is HTML but take note that manipulating the DOM, as you’ll know how to do here, does not change HTML. Rather, think of the DOM as the HTML the browser shows to the user.
Therefore, changing the HTML affects the DOM but changing the DOM does not affect the HTML.
There are two ways to manipulate the DOM. There’s the easy way: jQuery. However, I’ll teach you the harder way: Pure JavaScript.¹
So you might be asking, why learn the hard way? Why not just use jQuery?
jQuery is a nice library but it contains way too much stuff you won’t need. Even if you only needed three lines of code for your project to work, it will still download the whole jQuery file.
Yes, jQuery is less than 50kB in size but why have a lot when you only need a few?
Besides, it’s also nice to know how jQuery does it behind the scenes.
P.S. If you still insist on using jQuery, I recommend you read their own resource.
Some Pesky Assumptions
Before I proceed, I must first make some assumptions on your skills.
While not much JavaScript knowledge is needed, you must understand the concept of variables, functions, loops, and objects.
If you need some review, here is Mozilla’s resource on variables, loops, functions, and objects.
DOM Terms
Before I go on to the code, let’s first learn a few lingo. You’ll need it.
First up, we have the document. The document is kind of the HTML your browser displays to the user.
Therefore, if you haven’t manipulated the DOM yet, the document is the HTML you write.
So if your “Hello World” site is this:
<!doctype html>
<html>
<head>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
That in itself is the document.
Now, let’s get into some family lingo. (By the way I swear I’m not joking when I say these things).
As you can see, in between two HTML tags, sometimes there are other tags inside them.
For example, in the <html>
tag, it has the <head>
and the <body>
tag inside it.
<head>
and <body>
are considered to be the children of the <html>
tag. And since children must have a parent, we call <html>
the parent.
And since <head>
and <body>
have the same parent, each of them are siblings.
If you still think I’m joking about this, I won’t judge you. But that’s how we and JS call the relationships between the HTML tags.
Lastly, JS calls each tag and everything inside it a “Node”. But I’ll call it an element throughout this article instead. (Why? Well I want to. And it’s easier to understand for beginners.)
Manipulating the DOM
Now, let’s get to the exciting parts: we will now manipulate the DOM!
Note again that manipulating the DOM doesn’t manipulate HTML. It looks like it does but what it does manipulate is what the user sees in the browser.
Selecting Elements
Before we can manipulate the DOM, we must first tell JS which elements to manipulate.
To do so, let’s go through the syntax for a moment. Don’t worry if you don’t understand it yet.
const body = document.querySelector('body');
In the above example, we select the <body>
tag from the document and assign it to a variable named body.
Therefore the syntax for selecting an element is:
PARENT.querySelector('ELEMENT');
Notice that we don’t select the body by using '<body>'
. We use 'body'
instead. That’s because selecting elements is pretty much how you select elements in CSS.
Therefore, if we wanted to select the <head>
and <body>
tags, we can do this:
const head_and_body = document.querySelector('head, body');
However, if you tried to do the above and used console.log()
, you might notice it only gives out the head.
What gives? Well, querySelector()
only selects the first instance of the element you want to select. In other words, since most websites have the <head>
tag before the <body>
, using querySelector('head, body')
only gives out the <head>
tag.
But don’t worry! There’s another way! It’s querySelectorAll()
.
When using querySelectorAll()
, it hunts all <head>
and <body>
tags and stores them in a «Node List». While it looks and acts like an array, it is NOT an array.
For more information about the difference, this article explains it better.
So going back to our above example, if we wanted to select the <head>
and <body>
tags, we’ll use this:
const head_and_body = document.querySelectorAll('head, body');
Now, if we use console.log()
on head_and_body
, here’s what we get:
> console.log(head_and_body);
NodeList(2) [head, body]
> 0: head
> 1: body
> length: 2
> __proto__: NodeList
Ahh! Sweet!
Node Lists act like arrays so if we want to do something with the head tag, we just use head_and_body[0]
.
Now, there is another way of selecting elements: getElementById()
.
Since the id
attribute is unique to one element, you can forget about hierarchies and just zoom past the element.
For example, let’s say we have this HTML file and want to select the <p id='lorem'>...</p>
element:
<body>
<h1>Lorem ipsum dolor sit amet.</h1>
<p>Exercitationem blanditiis unde quas debitis enim quisquam facere quaerat autem placeat, repellendus dolor temporibus, tempora molestias suscipit pariatur atque officia consequatur voluptatum nemo recusandae consectetur fugiat itaque! Quae, praesentium reprehenderit.</p>
<p id='lorem'>Ullam nisi atque nesciunt aliquid quos a porro quis eum, maiores aspernatur placeat error numquam iusto qui dolorum culpa laboriosam repellendus consequatur. Quae temporibus non doloremque quos est voluptate beatae!</p>
<p>Commodi molestiae velit fuga earum, dolorem atque perspiciatis veritatis enim consectetur ratione praesentium.</p>
</body>
If we used the querySelectorAll()
method, we would have had this code:
const lorem = document.querySelectorAll('p')[1];
That isn’t necessary. We can simplify it down to:
const lorem = document.getElementById('lorem');
While we could have used querySelector('#lorem')
instead, using getElementById()
is more semantic.
Now, if there is a way to select elements by their id, there is also a way to select elements by their class. It’s getElementsByClassName()
.
I won’t go further into how to use this. If you have read this far, you know how to use that.
Like querySelectorAll()
, using getElementsByClassName()
gives out a Node List.
And there is another way to select elements: through their relationships with the other nodes.
For example, let’s take the following HTML:
<div>
<p>Text</p>
<ul>
<li>Unordered List 1</li>
<li id='2'>Unordered List 2</li>
<li>Unordered List 3</li>
</ul>
</div>
Assuming we already selected the div and assigned it to a variable named div
, we can select the <p>
tag by doing this:
const p = div.firstChild;
In the same way, we can select <ul>
by div.lastChild
(there is no secondChild
in JS).
Here are a couple relational selectors you can use:
- children > Outputs an HTML Collection²
- firstChild > The first child of the parent element
- lastChild > The last child of the parent element
- nextSibling > The next sibling of the element
- previousSibling > The previous sibling of the element
- parentElement > The parent of the element
Phew! That was a lot. To do a quick review, here’s an HTML file:
<body>
<h1>Hello World!</h1>
<p class='some-element'>Eligendi cupiditate corrupti temporibus vitae magnam laudantium quaerat a, voluptas earum. Odio illum enim ipsa illo beatae? Necessitatibus, temporibus adipisci!</p>
<p id='select-this' class='some-element'>Aspernatur corporis eum, qui dolorum illo rem harum pariatur esse voluptatibus fuga, incidunt deserunt officiis illum molestias voluptate. <span>Corporis,</span> illo.</p>
<p>Neque in quasi at architecto illum, suscipit ab incidunt tenetur, quo, rerum soluta earum aliquam dolore voluptatibus quod vel placeat!</p>
<p>Delectus fugit culpa iusto nesciunt, blanditiis aspernatur amet perspiciatis quaerat vel distinctio atque mollitia odio ex ratione quia? Tenetur, laudantium!</p>
<p>Eaque beatae placeat in quia dicta voluptatibus doloribus porro laudantium earum aliquam, perferendis laborum est alias debitis incidunt ratione delectus?</p>
</body>
To select the paragraph with the id ‘select-this’, here are almost all of the ways you can do so:
let p;
// Using querySelector()
p = document.querySelector('#select-this');
// Using querySelectorAll()
p = document.querySelectorAll('p')[1];
// Using getElementById()
p = document.getElementById('select-this');
// Using getElementsByClassName()
p = document.getElementsByClassName('some-element')[1];
// Using Relationships (I'll just stick to two for now)
const body = document.querySelector('body');
p = body.children[2];
const span = document.querySelector('span');
p = span.parentElement;
Creating Elements
The syntax for creating elements is simple:
document.createElement('ELEMENT');
So if you wanted to create a <p>
element and assign it to a variable, you use:
const p = document.createElement('p');
// The above will create <p></p>
That’s it. And while createElement()
creates an element, note that it doesn’t add it to the DOM itself.
Think of it as a lack of information. You’ve already created the element but don’t know where to put it.
So the next thing to learn is how to add them to the DOM itself.
Appending Elements
There are two ways we can add the element we create to the DOM. The first is this:
PARENT.appendChild(ELEMENT)
Since a new child will always be the youngest, appending an element using the above syntax will always be added just before the element’s closing tag.
Just for fun: What happens if we try to add an element to a non-closing element in JS (e.g. <img>
? Well you get this:
const img = document.createElement('img');
const p = document.createElement('p');
img.appendChild(p);
/*
<img>
<p></p>
</img>
*/
But don’t do that. That’s bad semantics.
Now, here’s the second way of appending the element to the DOM:
PARENT.insertBefore(ELEMENT, REFERENCE)
The above script will insert the element before the reference inside the parent node.
Confused? Here’s an example to make it a bit easier to understand:
<div>
<h1>Title</h1>
<p>Body</p>
</div>
JavaScript:
const div = document.querySelector('div');
const p = document.querySelector('p');
const blockquote = document.createElement('blockquote');
div.insertBefore(blockquote, p);
After running the script, the DOM will now become:
<div>
<h1>Title</h1>
<blockquote></blockquote>
<p>Body</p>
</div>
So what if we tried to insert an element inside a parent element but the reference isn’t in the parent?
You get an error:
Uncaught DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
Also, you cannot use document
as a parent. Not this time.
Deleting Elements
Now, sometimes we may want to remove some elements from the DOM. In these cases, we simply use the following syntax:
PARENT.removeChild(ELEMENT)
And yes. You cannot use document
as a parent. Again.
Manipulating Elements
Now, adding new elements isn’t enough. After all createElement()
just creates an empty element. What if we want to add attributes like classes or add text inside it?
JS has four ways in manipulating elements.
First, we can manipulate an element’s attributes.
For one, we can get an element’s attributes by using the following syntax:
ELEMENT.getAttribute('ATTRIBUTE NAME');
The above is pretty much self-explanatory so I’ll leave it at that.
If we want to remove an attribute (e.g. an alt tag) from an element, we can use the following syntax:
ELEMENT.removeAttribute('ATTRIBUTE NAME');
Lastly, we can set attributes within an element by using the following syntax:
ELEMENT.setAttribute('ATTRIBUTE', 'STUFF YOU WANT IN ATTRIBUTE');
So if you want to turn <p>
into <p class='paragraph'>
, you use this code:
const p = document.createElement('p');
p.setAttribute('class', 'paragraph');
Now, the second way we can manipulate the DOM is by manipulating its classes.
While we can add classes through the setAttribute()
method, it only allows us to add classes. Not remove them.
A more semantic way to add classes is to use the following syntax:
ELEMENT.classList.add('CLASS NAME');
But we can also manipulate an element’s classes by removing them using the following syntax:
ELEMENT.classList.remove('CLASS NAME');
And in turn, we can toggle a class (e.g. remove if it’s there, add if it isn’t) using the following syntax:
ELEMENT.classList.toggle('CLASS NAME');
So, the second (and better) way of turning <p>
into <p class='paragraph'>
is:
p.classList.add('paragraph');
The third way we can manipulate elements is by adding style rules.
These style rules will be ‘inline styles’ so I don’t recommend using them. If ever you want to change something for example, it may get confusing on whether you placed it in CSS or in JS.
But if ever you still want to go add style rules using JS, there are two ways of doing so.
If you only want to only add one style rule (e.g. color: blue
), you only have to use the following syntax:
ELEMENT.style.RULE = 'WHAT YOU WANT IN THE RULE';
Therefore, if we want to set a paragraph element to color yellow, we’ll do this:
const p = document.createElement('p');
p.style.color = 'yellow';
By the way, while CSS uses hyphens for spaces (e.g. margin-left
), JS uses camel casing (e.g. marginLeft
).
However, if you want to add several style rules, it’s much better to use the following syntax:
ELEMENT.style.cssText = 'WRITE HOW YOU WOULD WRITE INLINE CSS';
So if we wanted to set the paragraph to color yellow, have a padding of 10px, and the background black, we’d do this:
p.style.cssText = 'color: yellow; padding: 10px; background: black';
The last way we manipulate elements within a dom is by adding content within the element.
If you only want to add text content, you can use the following syntax:
ELEMENT.textContent = 'CONTENT';
However, if we wanted to add HTML content, we’d use the following syntax:
ELEMENT.innerHTML = 'CONTENT';
So if we wanted to add ‘Hello World!’ in the paragraph, we’d do this:
p.textContent = 'Hello World!';
// ALTERNATIVE WAY
p.innerHTML = 'Hello World';
DOM Events
Say you have a button and you want the background to turn green when it is clicked. How do you do that?
Well, since you only do something (turn the background green) when an event happens (click), we need to assign what we call an “Event Listener”.
Event Listeners are that: they listen to events. If you tell it to listen to a click, it will do so.
While there are three methods to apply event listeners, I’m only gonna focus on the best way.
The syntax is pretty straightforward:
ELEMENT.addEventListener('EVENT', () => {
// Stuff you want to happen
});
For the full list of events JavaScript can listen to, I recommend you go to Mozilla Developer Network’s Resource. There is way too much to cover.
Going back, if we wanted to have the background turn green when a button is clicked, we’d do the following:
// Assume there's a button in HTML and I assigned it the button variable. Assume the same for the body variable.
button.addEventListener('click', () => {
body.style.background = 'green';
});
Now, sometimes, we want the element to change itself. For example, instead of the background turning green, what if we wanted the button itself to turn green? Well, we could do this:
button.addEventListener('click', () => {
button.style.background = 'green';
});
However, there is another way of doing so. We use e
.
To demonstrate it’s use, if we wanted the button to turn green when it is clicked, we could instead use the following syntax:
button.addEventListener('click', (e) => {
e.target.style.background = 'green';
});
Why e.target
? Well, let’s do this first and see what we get:
button.addEventListener('click', (e) => {
console.log(e);
});
If you click on the button and go to the console, you will get something like this:
MouseEvent {isTrusted: true, screenX: 846, screenY: 609, clientX: 846, clientY: 516, ...}
As you see, e
is an object. If we look at one of its values, we get this:
target: button
Therefore, to manipulate the button itself, we must select target first before proceeding. Otherwise, you’ll be manipulating the object itself.
You might be asking though, what’s the point of using e
? Well, target
is only one value of e
. If we look at its other values, we can do other things with the DOM itself.
For example, one of e
‘s values is altKey
(which returns true
or false
). By using e
, we can tell JS to do something else when the user presses the Alt key while clicking the element.
Some DOM Problems and Solutions
Here is two DOM problems most people encounter when manipulating the DOM.
Adding Multiple Created Elements
Let’s say you want to create this HTML in pure JavaScript:
<ul>
<li>Hey!</li>
<li>Hey!</li>
<li>Hey!</li>
</ul>
If you are a beginner to DOM manipulation, you may have done something like this:
const ul = document.createElement('ul');
const li = document.createElement('li');
li.textContent = 'Hey!';
for (let i = 1; i <= 3; i += 1) {
ul.appendChild(li);
}
Now if you appended it to the body, you might be disappointed and end up with this HTML:
<ul>
<li>Hey!</li>
</ul>
What gives? Well, if you noticed, the variable li
is outside the for loop. JS treats it only as one element.
Therefore if you were to add it again in the same element (ul
), JS thinks it’s the same element you’re trying to add and instead only moves it.
So fixing it requires only one tiny change.
const ul = document.createElement('ul');
for (let i = 1; i <= 3; i += 1) {
const li = document.createElement('li');
li.textContent = 'Hey!';
ul.appendChild(li);
}
That will get what you want. Since the variable li
is recreated again every iteration, JS treats it as a new <li>
rather than the same one.
Node List Manipulation
Since Node Lists aren’t Nodes (I referred to them as Elements in here), you can’t just add attributes to them since you’ll be adding an attribute over the node list.
Node Lists act like arrays, so just use a loop to manipulate it.
However, there is another way to manipulate node lists: forEach()
. Using it looks like this:
const p = document.querySelectorAll('p');
p.forEach((e) => { // code // to do something with each element, use e as a placeholder. In other words: e.style.background = "blue"; });
Phew! That’s a lot of things we covered! And it’s not even all!
But don’t worry. You don’t need to know everything to be competent. These are like the 20% you need to learn in the Pareto Principle. It’s enough to let you do the 80% of projects you will work on.
Footnotes
- I lied. JavaScript isn’t the only way to manipulate the DOM. Most languages like Python can manipulate the DOM as well. However, JS is by far the easiest and least involved way of doing so.
- Also looks and acts like an array but is not an array.
SOURCE: https://medium.com/@pdlozano/how-to-manipulate-html-an-intro-to-the-js-dom-276784df6d61
Written by
David Lozano
Dangerously not dangerous. Student + Web Developer + Future Engineer