Week 9: JavaScript Functions and the DOM

This reading draws from a page by David Humphrey
and from the open textbook Fundamentals of Web Programming.

What is JavaScript?

This week we dive deeper into the third language of web development, JavaScript. Along the way, we will also learn more about HTML and CSS, especially how they are exposed to web developers by the browser.

We have been calling HTML and CSS languages for describing the content and display of web pages. JavaScript is a programming language, which means that it opens up to you the full power of the web browser — and computer. Knowing JavaScript offers advantages over knowing only HTML and CSS, at the cost of some complexity.

This week we will learn about several new features of JavaScript. This reading has three sections:

  1. its extends our ability to access a web page with JavaScript, both the content (HTML) and the styling (CSS)
  2. it moves beyond running JavaScript in the console to including JavaScript code in a web page
  3. it introduces the idea of a function, an essential tool for creating web pages that support interaction with the user and the browser

This reading uses several small web pages to illustrate ideas. There are links to these pages throughout the reading. You can also download this zip file that contains all the HTML and JavaScript file for the reading and follow along in your editor.

Accessing a Web Page with JavaScript: HTML and CSS

To work through this section, open up this web page, accessing-dom.html, in a separate tab or window. If you'd like to see the HTML for the file as you read, download the file to your computer and open it in VS Code.

Accessing HTML Elements

Last week, we learned that we can use JavaScript's querySelector() method to access an element in a web page by its HTML tag. For example, we can access the page's h1 heading and inspect its HTML with:

let h1 = document.querySelector('h1');
h1.innerHTML

We can also modify its attributes using an assignment statement:

h1.innerText = 'I am a little blue today';

Both of these snippets of code use a variable and an assignment statement. The variable is a name for a value. Recall that an assignment statement works like this:

  1. Evaluate the expression on the right side of the = (the assignment operator).
  2. Make the name on the left side of the operator refer to the value that results from Step 1.

Our ability to interact with HTML using JavaScript is bigger than just access by tag, though. Here are three additions to our set of tools.

First, we can also access an element by its class or id:

let element = document.querySelector('#main-content');
element
element.innerText = 'Here is my handle...';

This is a common use of JavaScript on a web page, especially with id's. They are unique and so allow us to target a specific element directly.

Second, there are several elements in a document that we can access directly, without using querySelector(). Two are the head and the body. These elements are required and unique, so the DOM exposes them to us as data properties:

let headElement = document.head;
headElement
let bodyElement = document.body;
bodyElement.innerHTML = '<h1>I am a little blue today</h1>';

Note that when we replace the innerHTML of the body, we overwrote the entire body, including the paragraph element. If we only want to change the h1 element, we need to target it directly.

Finally, we can add a class to an element by changing its className property. For example:

bodyElement.className
bodyElement.className = "myclass";
bodyElement.innerHTML = "<h1>Now I'm golden.</h1>";

Reload the playground web page before proceeding.

Accessing Style Attributes

In addition to accessing an element's content, we can also access its styling. Every element has a style data property. It is an object that contains all of the style information associated with the element. For example:

let bodyElement = document.body;
let bodyStyle = bodyElement.style;
bodyStyle
let h1 = document.querySelector('h1');
let h1Style = h1.style;
h1Style

Don't worry for now about the style object itself, which looks different than the objects we have seen thus far. We will learn about that kind of object soon.

For now, know that once we have access to an element's styling information, we can modify a specific style attribute with an assignment statement. Try this:

h1Style.backgroundColor = "lightblue";

Note: We can also access object attributes in succession without creating intermediate variables. For example, this statement changes the body's background color without creating variables for the body or its style attribute:

document.body.style.backgroundColor = "lightblue";

Putting all of our JavaScript knowledge together, we are able to target an element for new content and styling, perhaps in response to a user action. Here's a short sequence of JS statements to modify the page's main element:

myMain = document.querySelector('main');
myMain.innerHTML = "<h3>Where have all the flowers gone?<h3>";
myMain.style.height = "200px";
myMain.style.width = "200px";
myMain.style.backgroundColor = "lightgreen";

Taken together, these statements make a script, a short JavaScript program to perform a specific task. We can do more with code such as this than enter it into the console; we can also include it on our web pages for the browser to execute.

That is the topic of the next section.

Practice

Reload the page from this section in your browser. (If you would rather practice with one of your own web pages, feel free, just be aware that you may have to adjust the exercise a bit if your page has different classes and ids.)

  1. Change the background color of an element.
  2. Can you change the font size of an element to 48pt?
  3. Experiment with other style elements you have learned about in CSS. For example, see if you can change an element's margin or make it disappear (hidden).

Using JavaScript on a Web Page

So far, all of our JavaScript code has been written in a stand-alone form, executed in our browser's console (or in node.js).

Our ultimate goal is to be able to run our JavaScript code within web pages. To do that, we need a way to include the code in an HTML file. HTML isn't anything like JavaScript, though, so we can't simply type our JavaScript code in the middle of an HTML file and expect the browser to understand it.

Instead, we need an HTML element that can contain, or link to, our JavaScript code. HTML provides such an element: script.

When we learned CSS, we saw that we could include CSS code in our web pages in three different ways. We can use the script element to add JavaScript to a web page in two different ways.

Inline Scripts

First, we can embed a JavaScript program directly as the content of a script element.

Consider this web page, with the same HTML content as in the previous section plus this script element containing the JavaScript code from the end of of the section:

<script>
  myMain = document.querySelector('main');
  myMain.innerHTML = "<h3>Where have all the flowers gone?<h3>";
  myMain.style.height = "200px";
  myMain.style.width = "200%";
  myMain.style.backgroundColor = "lightgreen";
</script>

The browser loads the HTML from beginning to end, so it loads in the head, then the body, and finally the script. When it loads the script, it executes the JavaScript code, resulting in a new main section.

We can place internal scripts anywhere in an HTML file. However, it is common to put them at or near the end of the body. That way, the browser has loaded most or all of the HTML code before it loads and executes the JavaScript. That is essential in the case of our "myMain" script, because the code modifies an existing element in the HTML.

Exercise: Edit the inline-script.html so that the script appears in the head element or at the top of the body. Now reload the page. What happens? Why?

We can have as many script elements as we like in an HTML file. This will be useful when we have more than one form of user input, or when we want to use someone else's JavaScript code into our page.

External Scripts Linked via URL

For small bits of code, inline scripts are fine. They can also be useful when we are experimenting with new ideas. However, as scripts get larger, embedding them directly within the HTML file becomes unwieldy.

Furthermore, like CSS, JavaScript has a different purpose than HTML. It is useful to separate JavaScript code from HTML code and to putthem into their own files for a number of reasons:

  • The HTML becomes harder to read. Instead of looking at semantic content about the structure and content of a web page, we also have to read and understand programming logic. This can make it harder to understand the file, especially while debugging.
  • VS Code handles HTML and JavaScript equally well, even when they appear in the same file. However, there are many tools for working with HTML, and even more tools for working with JavaScript, that work only when given code the proper type. For example, web developers often use tools for "bundling" JavaScript code that is used on a website. Those tools can't do their job if the JavaScript is embedded inside with HTML markup.
  • Finally, browsers can cache files in order to improve the load time of a web site. When a large JavaScript file is embedded in an HTML file, the JavaScript code cannot be cached separately.

For these and other reasons, it is customary to move JavaScript programs to separate files with a .js file extension. We then tell the browser to load and execute these files using a different form of the script element:

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

Notice that, in this case, there is no content within the script element. Instead, the src attribute specifies a path to the script file. This is reminiscent of how the img element works, though script needs a closing tag because it can sometimes contain content.

With an external script, the browser begins by loading the .html file. When it encounters the <script src="script.js"> element, it loads the script.js file from the web server, and then runs the program it contains.

You can see this process at work in this version of the previous web page, with the JavaScript code now in a separate .js file. Notice that the script.js file contains nothing but JavaScript code, without the <script> tags. This is the same code that we created in our interactions with the web page within the browser's console.

External scripts do not have to reside on the same server as the web page. Like external images and third-party CSS style sheets, they can live on another server. To access them, we provide an absolute URL to the file.

Combining the Approaches

We can combine both of these methods, and include as many scripts as we need. The scripts we include can be:

  • inline, embedded within the HTML file
  • external, using a relative URL to a file on the same web server as the HTML file
  • external, using an absolute URL to a file on another web server

For example, consider three-scripts.html. It loads the same local external script.js file as before to modify the main element on the page. It also loads a remote external script that defines code for converting plaintext diagrams into graphics. The third script is an inline script that uses the code defined in the remote script to create a diagram on the page.

This page illustrates a common pattern:

  • loading an external script that defines objects, and then
  • writing an internal script that uses those objects to do something on the page.
(By the way, the typograms.js script is a cool way to create graphics within web pages. For more information, check out the Typograms homepage on Google's Github page.)

JavaScript Functions

Last week, we learned that we could use methods provided by JavaScript to compute values and take actions. For example:

❯ Math.sqrt(16)
4

❯ let text = '    Eugene    ';
undefined
❯ text.trim()
'Eugene';

❯ console.log('Area = 10');
[log] Area = 10
undefined

More recently, we have been using the querySelector() method to access information out of elements in a web page.

"Method" is a word associated with objects in many programming languages, including JavaScript. We can think of it as a synonym for function, a word familiar to us from our math courses. String.trim() and especially Math.sqrt() even resemble the sort of functions we saw in math courses: given a value as input, they return a specific value as output.

We'll sometimes need to write our own functions. For example, we will need to tell the browser what to do when a user takes an action, such as pressing a button or clicking the mouse. We will do that by writing one or more JavaScript statements that the browser can invoke when the user takes the action. We will package our JavaScript statements as a function, which the browser will call in much the same way that we invoke a method.

At its simplest, a function is a sequence of JavaScript statements like the ones in the script we saw earlier in the reading. However, instead of thinking of that snippet of code as five separate statements, we think of it as a single unit. We might think of it as "modify the main section".

This is an abstraction: stepping back from a bunch of details and thinking of them as one thing. This may seem unfamiliar to us when we think about in the context of JavaScript, but we all use abstractions in everyday life without thinking about them much at all.

For example, you may tell your friend, "I'm going to Web Development now." This is an abstraction of many smaller actions taken in sequence, perhaps something like:

  1. Leave your dorm room.
  2. Walk down the halls and stairs, and out of the dorm.
  3. Walk across campus to the Physics building.
  4. Enter the building and wait for the elevator.
  5. Take the elevator to the third floor.
  6. Enter Room 314 and find your favorite place to sit.

You would have a lot of long, boring conversations if you responded with all six of those steps every time a friend asked you what you were doing. You might even lose a lot of friends!

Your friend understands those steps are necessary when you say you are going to class, so the two of you can communicate in terms of the abstraction. Note that some of those six steps can be broken down into several smaller steps each, which would make the conversations even more tedious.

Defining a function is how we create an abstraction in Javascript code. A function is a portion of code that can be called by some other code, in particular the web browser. Functions can take values as input and may return a value or take an action.

We will use JavaScript functions in two ways. First, we will give a name to a set of repeatable steps that we can call in different ways and from different places in our scripts. Second, we will use them to define actions that can be performed in response to events, whether initiated by the user or triggered by the browser. We will explore both of these uses in the coming weeks.

JavaScript functions have other uses in web programming, but they are beyond the scope of this course.

If you would like to see an example of a function definition now, check out this page, which shows the inline script from the previous section changed to define a function named modifyMain(). The second script then calls the function to carry out the actions.

Don't worry about the details of the function definition at this point. Defining functions is one of the main topic for class this week.