{ Putting it all Together. }

Objectives

By the end of this chapter, you should be able to:

  • Build a simple web page using HTML, CSS and JavaScript
  • Understand when to use HTML, CSS and JavaScript

What is the DOM?

From MDN:

The Document Object Model (DOM) is a programming interface for HTML, XML and SVG documents. It provides a structured representation of the document as a tree. The DOM defines methods that allow access to the tree, so that they can change the document structure, style and content. The DOM provides a representation of the document as a structured group of nodes and objects, possessing various properties and methods. Nodes can also have event handlers attached to them, and once an event is triggered, the event handlers get executed. Essentially, it connects web pages to scripts or programming languages.

To access the DOM, we make use of the document object. This object has properties and functions that we use to access our HTML elements which we can manipulate with JavaScript.

Working with the DOM is one of the more fun parts about using JavaScript because we can add behavior to our web pages. With JavaScript, we can change how a page looks and functions based on events the user makes (clicking, hovering, submitting a form, typing a certain key). As you walk through the next couple sections, think about what you might be able to build with this new knowledge.

How to access elements in the DOM

Let's get started with this sample HTML. Copy and paste this into a file called index.html and add an external script tag, or use a <script></script> right before the body closes. For simplicity, we'll take the latter approach right now.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- make sure for now that your script tag is in the head -->
    <title>Document</title>
</head>
<body id= "container">
    <div class="hello">
        Hello World
    </div>
    <div class="hello">
        Hello Everyone!
    </div>
    <a href="#">This link goes nowhere!</a>
    <button>Click me!</button>
    <script>
        // you can put your JavaScript in here, or include an external file.
    </script>
</body>
</html>

The easiest way to select elements is by their id using the getElementById function on the document object (document.getElementById). This returns a SINGLE element (because ids must be unique!).

var container = document.getElementById("container");

We can also use a function called querySelector, which selects a SINGLE element using CSS selectors. If multiple elements match the query you pass in to querySelector, the function will simply return the first matching element that it finds.

var container = document.querySelector("#container");

Notice that when we select by id using querySelector, we pass in the string #container, not container. Remember: querySelector always expects a CSS selector. In contrast, when we use getElementById, we just pass in the string container (no hashtag)! Since getElementById expects to find an element by id, in this case the hashtag isn't necessary.

To select multiple elements, we can use getElementsByTagName or getElementsByClassName, or we can use querySelectorAll and pass in a CSS selector. These will return what appear to be arrays (they are not exactly arrays, but for right now, that is not a problem).

var divs = document.getElementsByTagName("div");
var divs = document.querySelectorAll("div");

Here is another example using getElementsByClassName and the same thing with querySelectorAll.

var divsWithClassOfHello = document.getElementsByClassName("hello");
var divsWithClassOfHello = document.querySelectorAll(".hello");

As you can see, when you pass in a class name using getElementByClassName, you don't need to start the string with a dot. The function expects to receive a class name - it's in the name of the function! On the other hand, querySelectorAll takes in any valid CSS query, which is why you need to pass in .hello if you want it to find all elements with a class of "hello."

In this section we'll continue to use the same HTML setup as before. Here we'll use it to examine some functions we can use to traverse the DOM. In case you need it:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- make sure for now that your script tag is in the head -->
    <title>Document</title>
</head>
<body id= "container">
    <div class="hello">
        Hello World
    </div>
    <div class="hello">
        Hello Everyone!
    </div>
    <a href="#">This link goes nowhere!</a>
    <button>Click me!</button>
</body>
</html>

Modifying properties and attributes on elements in the DOM

We can change the text of an element through the innerHTML property.

var firstDiv = document.getElementsByTagName("div")[0];

firstDiv.innerHTML = "Just changed!";

This can also be done using the innerText property.

var secondDiv = document.getElementsByTagName("div")[1];

secondDiv.innerText = "Just changed Again!";

What's the difference between innerText and innerHTML? You can find it here.

We can also directly manipulate the CSS properties for elements (through inline styling) with the style property.

var firstDiv = document.getElementsByTagName("div")[0];

firstDiv.style.color = "red";
firstDiv.style.backgroundColor = "teal";

Notice that if you're accessing CSS properties using dot notation, you need to camelCase those property names, since firstDiv.style.background-color is invalid JavaScript. (Bonus question: what do you think will happen if you try typing this in the console?) However, if you use brackets, you can write the properties the same way as you would in a stylesheet:

firstDiv.style["background-color"] = "purple"; // this works too

If we want to access/modify attributes on elements, we can do that with getAttribute and setAttribute:

var body = document.getElementById("container");

body.getAttribute("id"); // "container"
body.setAttribute("id", "new_container"); 
body.getAttribute("id"); // "new_container" 

We can also add and remove classes to elements using classList

var secondDiv = document.getElementsByTagName("div")[1];

secondDiv.classList; // ["hello"]
secondDiv.classList.add("another_class"); 
secondDiv.classList; // ["hello", "another_class"]
secondDiv.classList.remove("hello"); 
secondDiv.classList; // [another_class"]

Notice here that we use methods called add and remove, not the push and pop methods we've seen when dealing with arrays. This is because the classList isn't actually an array and doesn't have push or pop methods on it.

Nodes vs Elements

When you read documentation about the DOM, you will see the terms node and element. In JavaScript these two are different and you can read more about the difference here. In short, there are many different types of nodes, but the ones we will most often be working with are ELEMENT_NODE and TEXT_NODE (you can see them all here). Element nodes are HTML elements (div, span etc.). Text nodes are the actual text of an element node.

Traversing the DOM

Another very common operation when working with the DOM is trying to find elements inside of other elements. When we travel through the DOM in search of something, we are doing what is called DOM traversal. Here are a few common methods we can use for finding elements and/or text nodes in relation to an element that we have already found.

var container = document.getElementById("container");
container.childNodes; // // this contains all of the nodes (including text nodes) that are children
container.childNodes.length; // 11
container.children; // this contains all of the elements that are children of the element we have selected
container.children.length; // 5

var link = document.querySelector("a");
link.parentElement; // <body id="container">...</body>
link.previousElementSibling; // <div class="hello">Hello Everyone!</div>
link.previousSibling; // text node
link.nextSibling; // text node
link.nextElementSibling; // <button>​Click me!​</button>​

Creating elements

To create elements we use the .createElement function on the document object and pass in a string with the name of the element that we would like to create. This will just return a new HTML element without any text/attributes or placement on the page!

var newDiv = document.createElement("div");

So now that we created this element, how do we place it on the page?

Appending elements

var button = document.createElement("button");
button.innerText = "I am a button created with JavaScript!";

var container = document.getElementById("container");
container.appendChild(button);

And now that we created an element, how do we remove one?

Removing elements

var linkToBeRemoved = document.querySelector("a");

var container = document.getElementById("container");
container.removeChild(linkToBeRemoved);

Changing multiple elements

So what would happen if we wanted to change all of our divs to have a background color of red? Unfortunately, we can not do that without a loop:

var divs = document.querySelectorAll("div");
divs.style.backgroundColor = "red"; // this will not work, try to understand the error you receive!

// we have to use a loop for each one instead.
for(var i = 0; i < divs.length; i++){
    divs[i].style.backgroundColor = "red"; // this will work!
}

Throughout this chapter we'll be using the following HTML as our working example. Feel free to copy and paste this text into your text editor, then open the page in Google Chrome.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button class="first_button" onclick="firstClick()">Click me first!</button>
    <button class="second_button" >Click me second!</button>
    <button class="third_button">Click me third!</button>
</body>
</html>

What's an event?

From MDN

DOM Events are sent to notify code of interesting things that have taken place. Each event is represented by an object which is based on the Event interface, and may have additional custom fields and/or functions used to get additional information about what happened. Events can represent everything from basic user interactions to automated notifications of things happening in the rendering model.

In a nutshell, events are things we can trigger by interacting with the browser. Here are some potential events we can listen (and then execute some code) for:

  • clicking on something
  • hovering over something with the mouse
  • pressing certain keys
  • when the DOM has loaded
  • when a form is submitted

You can see a full list here.

Adding an event listener

There are three ways that we can add event listeners to our code. Given an element we want to listen to, and a function we want to execute, we can either attach the name of the function to the element in HTML, in JavaScript, or we can use the addEventListener method.

Here's some JavaScript code that highlights each one of these approaches:

// Option 1: - directly in HTML. Note that in your HTML, 
// the first button makes reference to a function called firstClick 
// in the onclick attribute
function firstClick(){
    alert("you clicked the first button!");
}

// Option 2 - attach the function to the element
var secondButton = document.querySelector('.second_button');
secondButton.onclick = function(){
    alert("you clicked the second button!");
}
// Option 3 - use addEventListener
var thirdButton = document.querySelector('.third_button');
thirdButton.addEventListener("click", function(){
    alert("you clicked the third button!");
});

So which one is best?

It is often best to either use the second or third option, as these are a bit more customizable, but it is good to know all of your options. In this course, we will primarily be using addEventListener.

Let's go back to our initial example, remove the onclick attribute from our first button, and add some event listeners to our buttons.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <button class="first_button">Click me first!</button>
    <button class="second_button" >Click me second!</button>
    <button class="third_button">Click me third!</button>
</body>
</html>

Just like when changing multiple elements, we need to iterate through each one - we cannot just add an event listener to an array-like object of element nodes.

var buttons = document.getElementsByTagName("button");

// this will NOT work - what kind of error do you get?
buttons.addEventListener("click", function(){
    alert("You just clicked a button");
});

// we have to add the event listener to each button
for(var i=0; i<buttons.length; i++){
    buttons[i].addEventListener("click", function(){
        alert('You just clicked on a button!');
    });
}

Removing an event listener

Sometimes we want to stop listening for events. To do that we use the removeEventListener method, but it has some quirks you should be aware of. Assuming you've still got those alert messages being triggered when you click on a button, let's take a look at the following code:

var buttons = document.getElementsByTagName("button");

// this will NOT work 
buttons.removeEventListener("click", function(){
    alert("You just clicked a button");
});

// we have to remove the event listener to each button but this STILL won't work! That is because we are using an annonymous function
for(var i=0; i<buttons.length; i++){
    buttons[i].removeEventListener("click", function(){
        alert('You just clicked on a button!');
    });
}

In order to successfully remove an event listener, the callback that we pass in can't be an anonymous function. Let's clear out all the JavaScript and start from scratch. The code below should work; if you run it, event listeners will be added and then removed, so that nothing happens when you click on a button.

// In order to remove, we have to name our function before adding it instead of adding an anonymous function
function alertData(){
    alert("You just clicked a button");
}

for(var i=0; i<buttons.length; i++){
    buttons[i].addEventListener("click", alertData);
}

// since we have named our function, we know which one to remove
for(var i=0; i<buttons.length; i++){
    buttons[i].removeEventListener("click", alertData);
}

Adding an event to wait for the DOM to load

Another issue that we may face is when we have code like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script>
        var container = document.getElementById("container");
        container.innerText = "Hello World"; // This throws an error!
    </script>
</head>
<body>
    <div id="container">

    </div>
</body>
</html>

What's going on here?? Well, since our script tag is running before the DOM has loaded, it has no idea what the <div id="container"></div> is! What we can do is wait for the DOM to load before executing any JavaScript. This can be done either with the load event or the DOMContentLoaded event. You can read about the difference between them here.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script>
        document.addEventListener("DOMContentLoaded", function(){
            var container = document.getElementById("container");
            container.innerText = "Hello World"; // This works now!
        });
    </script>
</head>
<body>
    <div id="container">

    </div>
</body>
</html>

You should get in the habit of always waiting for the DOM content to load before you try to manipulate anything on the page with JavaScript.

When you're ready, move on to Full Stack Web Development

Continue

Creative Commons License