Working with the DOM

The document is a global object that contains the content of a webpage.

With Javascript, you can select and change parts of the webpage by interacting with the DOM or its also known as the Document Object Model.

Understanding how interaction with Javascript works requires an understanding of the DOM. Think of the DOM like a tree-like structure. In a webpage, you have a head and body. Nested inside those you have other elements. The head contains the title node and the body can contain headings, paragraphs, ul elements with li elements nested inside of them.

<html>
    <head>
        <title>Javascript</title>
    </head>
    <body>
        <h1>The DOM</h1>
        <p>My first paragraph.</p>
        <ul>
            <li>one</li>
            <li>two</li>
            <li>three</li>
        </ul>
    </body>
</html>

The document node would be the root of the tree. The head and body nodes sprout like branches leading to other branches or nodes. At the top of the tree are leaves represent the most deeply nested nodes like the headings or li tags. In computer science, these trees are drawn from top-down more like a family tree. The relationships connecting nodes are also described in family terms. For example, the body is the parent of the headings paragraphs and ul nodes while also being a sibling of the header node. The li nodes are siblings of each other and at the same time children of ul node.

Selecting your first element

This is the same HTML we seen before only this time I’ve added the extra id attribute to our h1 tag. With a few lines of Javascript, I’m going to change its colour by clicking on it.

<html>
    <head> 
        <title>Javascript</title> 
    </head> 
    <body> 
        <h1 id="myHeading">The DOM</h1> 
        <p>My first paragraph.</p> 
        <ul> 
            <li>one</li>
            <li>two</li> 
            <li>three</li> 
        </ul>
    </body>
</html>

I started by selecting the element you can do this with a variety of different ways here I’ve used document.getElementById and passing it our heading. This is stored in the const called myHeading. This means I can reference the header later in my program,

const myHeading = document.getElementById('heading');

I then make our heading listen for mouse clicks by using the addEventListner method. There are other events you could listen for such as keypresses, scrolling or mouse moves etc but for this example I’ll stick with the click.

myHeading.addEventListener('click',() => { 
    myHeading.style.color = "blue";  
});

With the addEventListener, I’ve added in the first parameter ‘click’ the event we want to listen for. Then I pass is the second parameter which is a function. This way whenever the heading is clicked the function can tell the browser what to do. Inside the function, I use the myHeading const again and access its style property and then its colour property using dot notation and giving it he string value blue. And it’s as simple as that we’ve selected the element, added the click event and told the browser to do something whenever the element is clicked.

Selecting Elements by Tag

We already know how to select any element by an ID but what if there is no ID or you want to select multiple elements. That’s where you can use the document get Elements By Tag Name method. It’s very similar to the document get Element By Id method we already used but with one difference. It contains the word Elements, not Element. Not only would you get an error if you mixed these up but each one returns a different data type. The document.getElementById returns a single element the document.getElementsByTagName returns a collection of elements. A collection is like an array you can access each element in the collection passing the index value or you could even loop over the collection to access all of them for example.

<p>Things that are purple:</p>
<ul>
    <li>grapes</li>
    <li>amethyst</li>
    <li>lavender</li>
    <li>plums</li>
</ul>

<script>
    const list = document.getElementsByTagName('li');
    
    list[0].style.color = blue;
    for (let i = 0; i < list.length; i += 1) {
        list[i].style.color = blue;
    }
</script>

I’ve created a new const called list and give it all our li tags by using the document get Elements By Tag Name method. Once I’ve got the collection of elements I can a select each one by using its index value. I’ve initially changed grapes to blue by giving its index value 0.  To change all li tags to blue I’ve iterated over them all with a for a loop. I then access each item in the loop by using the index value i inside the loop and changing each of there properties too blue.

Selecting Elements by Class

When working with HTML often you’ll see classes being used to add styling to the frontend. With Javascript, you can select them using the document get Elements By Class method. It works similar to the document get Elements By Tag Name method in that it will return a collection. See this in action below where I’ve selected my favourite items from a list.

<p>Things that are purple:</p>
<ul>
    <li class="favourite">grapes</li>
    <li>amethyst</li>
    <li class="favourite">lavender</li>
    <li>plums</li>
</ul>

<script>
    const favourites = document.getElementsByClass('favourite');

    for (let i = 0; i < favourites.length; i += 1) {
        favourites[i].style.color = red;
    }
</script>

In the example, we’ve added the class favourite to our list items. Then I’ve created a new const ‘favourites’ and give it all our elements with the favourite class by using the document get Elements By Class method. Similar to the document get Elements By Tag Name method this returns a collection that I then iterate over with a loop to access each item and change its colour property to red.

QuerySelector

QuerySelector and querySelectorAll are the most flexible of the DOM selectors because they accept IDs, classes, tag names and the pseudo-classes.  The difference between the two is that the querySelector() method returns the first match of a given CSS selector. The querySelectorAll() method returns all elements in the dom that matches a CSS selector and returns them as an HTML collection. For example, let’s highlight all of the even the list items so they are easy to read.

<p>Things that are purple:</p>
<ul>
    <li class="favourite">grapes</li>
    <li>amethyst</li>
    <li class="favourite">lavender</li>
    <li>plums</li>
</ul>

<script>
    const items = document.querySelectorAll('li:nth-child(even)');

    for (let i = 0; i < favourites.length; i += 1) {
        items[i].style.backgroundColor = lightGray;
    }
</script>

In the example, I use the nth-child pseudo-class selector to get all the even list items and store them in the items const. Just like with the previous selectors I expect to get a collection of HTML Elements so I can iterate over each item. With access to each item, I can change its properties. In this case, I’ve changed the background on the even list items only to a light grey. You could easily select the odd items also by changing the even keyword to odd in the pseudo-class selector.

Setting Content with textContent & innerHtml

The textContent method will set or return the text content of the specified selector. If you set an elements text value using the textContent method any child elements are removed and replaced by that single text string. Take a look a the textContent Method in action below.

<h1>My Favourite Things</h1>
<ul>
    <li class="favourite">cookies</li>
    <li>carrots</li>
    <li class="favourite">ice-cream</li>
    <li>cabbage</li>
</ul>

<script>
    const myHeading = document.querySelector('h1');
    myHeading.textContent // Returns My Favourite Things
    myheading.textContent = "My Favourite Foods";
</script>

I’ve selected the h1 heading element using the querySelector. First I read the headings text value with myHeading.textContent by not assigning it a value it will return the current value. Now if I assign a new string to the textConent it will update the heading. I do this by using the = operator and the new string value in this case ‘My Favourite Foods’. One other way to alter content is called innerHTML. If I were to swap out textContent with innerHTML it will function the same way. However, there is one difference. It can also read and alter the elements on the webpage. You can essentially replace everything between an elements opening and closing tag for example.

<ul>
    <li class="favourite">cookies</li>
    <li>carrots</li>
    <li class="favourite">ice-cream</li>
    <li>cabbage</li>
</ul>

<script>
    const list = document.querySelector('ul');
    
    list.innerHTML //Returns 
    <li class="favourite">cookies</li> <li>carrots</li> <li class="favourite">ice- cream</li> <li>cabbage</li>
    list.innerHTML = "<li>Apples</li>";
</script>

I store the ul tag in a list const using the querySelector(‘ul’) method. Then I call the innerHTML method on our list const & similar to the textContent if we don’t assign a new value it will return a string of HTML that’s made up of all the li tags inside our list. I then assign a new value Apples using innerHTML. This doesn’t add apples to the current HTML content but instead replaces it. So now there is only one item in our list.

Changing Elements Attributes

An elements attribute like the href on a link or type on a input are all properties on the element object. You can access and update that information just like our previous selector’s let’s take a look.

<!DOCTYPE html>
<html>
<body>

<h2>HTML Forms</h2>

<form action="/action_page.php">
  <label for="fname">First name:</label><br>
  <input type="text" class="form-element" id="fname" name="fname" value="John"><br>
  <label for="lname">Last name:</label><br>
  <input type="text" class="form-element " id="age" name="age" value="Doe"><br><br>
  <input type="submit" value="Submit">
</form> 

<script>
  const input = document.querySelector('#age');
  input.type; // returns "text"
  input.className; // returns "form-element"
  input.type = 'number';
</script>

</body>
</html>

In the example above I’ve a simple HTML form that captures some data on a user. The age field type should be number, not text, with javascript I can change that. first, I select the input using the document.querySelector and store it in a variable called input. I can see the current input type by accessing that property by using input.type. If you don’t assign a new value the input.type will return as a string. The attribute with an exception to this rule is class. To access the class property you have to use className, it too will return the current className.  To update the input.type attribute you add the = operator with you new value. In our case I want our new input type to be a number so the field value reflects the data being captured in the form.

Styling Elements

Like any property of an HTML Element, you change the styles of an element using Javascript. A style set in this way overrides the styles written in the external style sheets.  However, unlike most other attributes, the style property itself is an object. The properties on a style object represent the various CSS properties I can set for example.

const title = documentQuerySelector('#title');
title.style.color; // returns title colors
title.style.color = red; // sets the color property

In the console, I’ve selected an element with the title ID. Then if you type title.style you will see all of the style objects properties like font size and colour etc. One question you may have is why can’t I see my current styles? That’s because these style properties only reference the elements inline styles so any rules that exist are separate from this document. This means you can’t rely on these javascript properties to completely style your elements. The style objects properties are generally used as a setter and not getters for this reason.

Creating New DOM Elements

So far we’ve made quite a bit of changes to existing dom elements with Javascript. But you can also add create new elements. You create new elements with the document.createElement and passing the name of the HTML element you want to create as a string for example.

const btn = document.createElement("BUTTON"); // Creates a <button>
btn.innerHTML = "CLICK ME"; // adds text to button

Appending Dom Elements

Creating an element doesn’t add them to the dom you need to add them. At the start of this post, I mentioned the DOM being like a tree with branches acting like nodes. If you visualize the concept again by adding a new element you adding a new branch or node to that tree. You do that by selecting an existing node and appending it using the appendChild method for example.

<!DOCTYPE html>
<html>
<body>

<ul id="shopping-list">
  <li>Coffee</li>
  <li>Bread</li>
  <li>Milk</li>
</ul>

<button onclick="addToList()">Add Item to List</button>

<script>
function addToList() {
  var node = document.createElement("LI");
  node.innerHTML = "Water";
  document.getElementById("shopping-list").appendChild(node);
}
</script>

</body>
</html>

First, I create an LI node then use the innerHTML method to add text to that li tag. Finally we append the LI node to the list. One thing that initially struggled was understanding the difference an element and a node. The difference is that the node already belongs to the DOM while elements are just plain HTML.

Removing Nodes

One of the last things you need to know to when changes to the DOM is how to remove nodes. You can remove nodes by using the removeChild() method. Just like the appendChild, the removeChild takes a child element as an argument, though when using this your want to remove it from the parent instead of adding for example.

<!DOCTYPE html>
<html>
<body>

<ul id="shopping-list">
  <li>Coffee</li>
  <li>Bread</li>
  <li>Milk</li>
</ul>

<button onclick="removeItem()">Add Item to List</button>

<script>
function removeItem() {
  const shoppingList = document.getElementById("shopping-list");
  const lastItem = document.querySelector('li:last-child');
  shoppingList.removeChild(lastItem);
}
</script>

</body>
</html>

In this example, I select the shoppingList using the document get element by ID method. Then I select the list item I want to remove using the document query selector and the last-child pseudo-selector. And Finally I use the remove child method on our shopping list const and pass it the node we want to remove from the dom.

Wrapping Up

This blog post covered a lot of Javascript to help you make your web pages more interactive. We can select elements, change those elements, create elements, append them and remove them. There still more we can learn to take them to another level by using events listeners but that’s a blog post for another time. Thank you for reading, and being part of the discussion. If you would like to chat or have any questions drop me an email I’m always happy to talk.