The DOM and How To Use It

If you've hung out in web developer communities like our Discord for a little while then you've probably heard of the DOM. But what is the DOM? And why should you care about it?

The DOM (Document Object Model) is a representation of a web page as a hierarchical structure of nodes. Each node has properties and can be the 'parent' of an unlimited number of 'child' nodes. You can think of it like a tree or a bullet-point list:

* HTML
    * HEAD
        * TITLE
        * LINK
        * SCRIPT
    * BODY
        * H1
        * P

Your web browser uses this tree-like representation to render web pages in a way that makes sense to you. Normally, this process is invisible and we don't need to think about it much. However, if we want to use Javascript to manipulate the structure and elements of a web page, it's important to have a basic understanding of how the DOM works.

The DOM in Practice

For this tutorial, you'll want to go to the web-101 git repository and clone or download the examples there. We'll go through the 'First Webpage' and 'First Form' examples and discuss the code in them.

Setting Up Our Webpage

Our First Webpage demo shows how to set up a project with HTML. We're going to look at a couple of lines from it. The first is the <script> tag in the <head>.

<script src="script.js"></script>

This tells our browser where to find our script file so that we can begin to embed Javascript in our page. In our case it's the same folder and we don't need to do anything unusual. However, <script> tags also let us bring in code written by others. For example, this is how to include the current version of the popular JQuery library in our page.

<script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>

Moving on, let's look at the other important line of HTML from this project.

<button onclick="hello()">Click me!</button>

This creates a button and assigns the hello() function from script.js to its onclick event. In simple terms, it tells the button what to do when it's pressed. So what does hello() do? Let's head over to script.js.

function hello(ev) {
    alert("Hello world!");
}

The function hello takes one argument, ev. When the button is pressed, it passes an Event object to the function. This contains some useful data and functions for dealing with the click event. In our next example, we'll look at what to do with this.

Finally, the alert() function makes a box pop up for the user with the string we specify.

Validating a Form

Next, we'll take a look at the First Form demo. This is a bit more complicated than our first example. One thing that's interesting is that we don't need to do many complicated checks on the content of our forms using Javascript: HTML 5 added default checks which will work in most cases.

<form id="infoForm">
    <input id="name" required placeholder="Enter your name" type="text"></input>
    <input id="email" required placeholder="Enter your email" type="email"></input>
    <textarea id="feedback" placeholder="What did you want to tell us?" rows="4" cols="50"></textarea>
    <input type="submit">
</form>

The two attributes that we care about here are required and id. The former tells the web browser that this field is needed for a valid form entry. The latter allows us to access the DOM elements from our script. Unlike our previous script you'll notice that no events are set directly on form elements.

Let's take a look at our JavaScript file. At the top are two functions: formOnSubmit and showInvalid. We'll take a look at these later. The first line that is actually run is the following:

window.onload = (ev) => {}

This line assigns a function to run when the webpage finishes loading. This is a great place to do any setup required for other interactions on your webpage, like these:

nameElement = document.getElementById("name");
emailElement = document.getElementById("email");
feedbackElement = document.getElementById("feedback");
formElement = document.getElementById("infoForm");

Remember the id attribute? Using document.getElementById we can use it to get any element on our webpage. id is guaranteed to be unique between different DOM elements: your browser will complain if it isn't.

The other part of our setup function follows:

formElement.addEventListener("submit",formOnSubmit);
nameElement.addEventListener("invalid",showInvalid);
emailElement.addEventListener("invalid",showInvalid);

This is similar to the onclick parameter we looked at in our first demo. It adds an event listener to the three elements:

  • When the form is submitted (i.e. the submit button is clicked or you press enter on the form) and the data is good, formOnSubmit is called.
  • When the form is submitted and the name or email fields are invalid (either empty, or not an email), showInvalid is called.

addEventListener("submit",formOnSubmit) and formElement.onsubmit = formOnSubmit aren't the same. In the first case, we're adding a single listener to the submit event (and could add more). In the second case, we're setting the submit event to call formOnSubmit (and so can only use it once).

Now let's look at each of the functions which we call to see what they do. First is showInvalid. This function only has one line.

ev.target.classList.add("invalid");

ev.target is whichever element happened to call the event which triggered the function. In our case, it's either nameElement or emailElement.

classList is the set of CSS classes attached to a given element. We call the add function to add the invalid class to the element. invalid looks like this, and sets a red border on our invalid text boxes:

.invalid {
    border-color: red;
    border-width: 5px;
}

Finally, let's look at the formOnSubmit function.

ev.preventDefault();

By default, submitting a form will reload the page or redirect you to another page. A lot of the time, especially in web apps, this isn't the behaviour we want. ev.preventDefault() suppresses that behaviour.

alert(
        [
        `Name: ${nameElement.value}`,
        `Email: ${emailElement.value}`,
        `Feedback: ${feedbackElement.value}`].join('\n')
)

element.value just gives us the value of a form element. Here we use it (with a little string magic) to output our three fields in a nice alert box.

Note that the value of a form element is not always the same as what you see. This is important to remember when dealing with more complex form elements like radio buttons, checkboxes, or drop-downs.

After we print out the values of our fields, clear them to give the user feedback that input was successful.

nameElement.value = "";
emailElement.value = "";
feedbackElement.value = "";

Finally, we remove the red borders from our name and email boxes.

nameElement.classList.remove("invalid");
emailElement.classList.remove("invalid");

Feel free to experiment with either or both demos to make them do whatever you like.

Assignment

  • Add a new required field for a website address to the form using the url input type. Give it the same behaviour as the name and email fields and make sure it's outputted in the alert box.
  • Add an expected salary field (required) which uses the number input type.
  • Add a new class to the CSS to show when an input is valid. Use the change event to flag a field as valid or invalid as soon as the user unselects it. You can use nameElement.checkValidity() to avoid writing your own validation code.
  • Put some reasonable constraints on the salary field and alert() the user if their salary falls outside the expected range.

Project

Write an app which simulates a traditional calculator with +, -, /, *, =, and C functions. It should incorporate HTML buttons and make use of their onClick event. You can either use onclick= or addEventListener to hook up the buttons.

Note that while you can use the input.text field to set the value of the calculator's "display", it will usually look better if you use a <p> or <span> element and use the element.innerText field to change the text.

As an extension you can try incorporating other functions like square root, percentage, M+, or decimal points.