Week 11: JavaScript Collections

This reading draws from pages
by John Davis and David Humphrey.

Table of Contents

This week we dive deeper into JavaScript. This reading has two main goals:

  1. learn how the browser makes sets of DOM elements available to us as JavaScript collections such as arrays
  2. learn how to work with JavaScript arrays, both individually and collectively with loops

A secondary goal of this reading is to take another look at JavaScript strings, which are collections, too, and which interact with arrays in some useful ways.

The section on arrays uses this file as a running example. Control-click on the link to download the source file, and have it ready to use and read later.

The DOM and Collections of Objects

We have seen that the browser not only renders a web page for us to view. It also exposes the page to our JavaScript code as a hierarchy of objects:

a graphic showing the DOM for a simple web page
This tree of objects represents a page with a standard head and a body consisting of a div and a script element.
The div contains an image, a heading, a paragraph, and another div.

The Document Object Model (DOM) connects web pages to our scripts by representing the structure, content, and styling of a document. We access the DOM with JavaScript code.

The DOM tree is an object. The tree is a model of the web page where each element on the page is an object stored as a property in the tree.

In the example in the diagram above, the body is an object that contains objects for the div and the script element. The div contains objects for the image, the heading, the paragraph, and another div.

Each of those objects holds a representation of the corresponding HTML element:

a graphic showing the HTML element modeled by the image object in the DOM tree
The img object in the DOM is a model of the HTML element in the web page, with all the same attributes as the HTML.

Working with DOM Objects

An object is an abstraction of some entity we can describe or talk about. It has data properties and methods. We access an object's properties and methods using the . notation, for example, aString.length or Math.sqrt(). We can also change the value of property using an assignment statement. An method is a function we can call; it's a function that belongs to an object and has access to its data properties.

Any function we create becomes part of the window object that sits at the top of the DOM tree for a page. That object also has built-in functions, including the alert() and prompt() functions we've used.

As we've seen, we can interact with the DOM in many ways:

  • We can access any element in the tree:
    let element = document.querySelector("p");
    
  • We can access and modify an element's text content:
    element.innerText = "new text";
    
  • ... or its HTML content:
    element.innerHTML = "new <em>text</em>";
    
  • We can access any attribute of an element using getAttribute():
    let imgSource = element.getAttribute("src");
    
  • ... and modify an attribute's value using setAttribute():
    element.setAttribute("src", "newImage.jpg");
    
  • We can access an element's id or class attributes directly as properties:
    element.id
    element.className
    
  • Finally, we can access and modify an element's styling through its style data property:
    element.style.background = "red";
    

Getting Collections of Objects from the DOM

Up to now, we have been limited to working with single values: the first p element in a document, or the only class name associated with the element. We know, though, that a document may have many p elements, or td elements, or li elements. We also know that a class can have more than one class associated with it.

The DOM exposes these features to us as a collection of objects. This collection takes the form of a JavaScript array. We can think of an array as a list of objects that we can access by their position.

Here are two ways to get a collection from the browser:

  • We can access all of the p elements in the tree using the querySelectorAll() method:
    let paragraphs = document.querySelectorAll("p");
    
    The object returned by querySelectorAll() is a collection: a list of elements in the DOM that match the given selector.
  • We can access all of an element's classes through its classList data property.
    let allClasses = element.classList;
    
    The classList property is also a collection.

We can interact with these collections in a number of ways: access individual items in the list, add items to the list, remove items for the list, and so on.

Let's learn more about JavaScript arrays so that we can interact with our web page in more useful ways.

JavaScript Arrays

A JavaScript array is a collection of objects. We can treat an array as a group, and we can access the members of the collection individually by their position in the list. Arrays have many useful properties and methods that we can use when working with lists in JavaScript.

Array Basics

As with other JavaScript data types, we can create an array literal in our scripts. We use square brackets, [...], to indicate an array:

❯ let hobbits = ["Merry", "Pippin", "Frodo"];

In this example, hobbits is a collection containing three elements:

❯ hobbits
["Merry", "Pippin", "Frodo"]

Like strings, arrays have a data property that stores the size of the collection:

❯ hobbits.length 
3

We can access the objects in the array using the same square brackets. The items are numbered 0, 1, 2, ....

❯ hobbits[0]
"Merry"
❯ hobbits[1]
"Pippin"
❯ hobbits[2]
"Frodo"
❯ hobbits[3]
undefined

Note that hobbits[3] is undefined. There are only three items in the collection, and hobbits[3] refers to a fourth item.

We can also modify the objects in an array using an assignment statement:

❯ hobbits[1] = "Samwise Gamgee";
"Samwise Gamgee"
❯ hobbits
["Merry", "Samwise Gamgee", "Frodo"]
❯ hobbits.length
3

Now, hobbits[1] contains the value "Samwise Gamgee". The rest of the array, including its length, are unchanged.

Receiving Collections from the DOM

Creating arrays in our code can sometimes be quite useful. It is much common, though, for us to work with arrays that we receive from the DOM.

Consider this web page. (Download it now you haven't already.)

We have been using the querySelector() method to retrieve an element from the DOM. But there are four paragraphs in this document, and querySelector() returns only the first of them.

The querySelectorAll() method enables us to retrieve a collection of all elements in a web page that match the selector. For example:

❯ let allPs = document.querySelectorAll('p');
undefined
❯ allPs
NodeList [<p>, <p>, <p id="handle">, <p>]

We can access individual elements in this collection using the same square brackets:

❯ allPs[0]
<p>I'm a little teapot.</p>
❯ allPs[2]
<p id="handle" class="blue border padding">Here is my handle.</p>

Another common way to receive a collection from the DOM is to ask an element for a list of the classes defined on it. Earlier, we saw that DOM elements have a classList data property. It is a collection:

❯ let specialP = allPs[2];
❯ let classes = specialP.classList;
❯ classes.length
3
❯ classes[0]
"blue"
❯ classes[1]
"border"
❯ classes[2]
"padding"

We can work with collections of classes and collections of elements in the DOM using other features of JavaScript arrays.

Methods for Querying and Modifying Arrays

One of the common uses of JavaScript on a web page is to add or change styling on an element. We can do so by adding a class to the element's classList or removing a class from the list.

Suppose we wanted to add a blue background to the first paragraph on the web page. We can use the array method add():

❯ let allPs = document.querySelectorAll('p');
❯ let firstP = allPs[0];
❯ firstP.classList.add('blue');

To remove an element from an array, we use the method remove():

❯ allPs[2].classList.remove('border');

These methods are a handy way for a script to change the styling on a web page.

Iterating Over the Items in a Collection

Sometimes, we would like to take some action on every element in a collection. Last week, we learned that the while statement gives us a way to 'loop' over an action. We can use a while statement to take the same action on each element in an array.

Recall the basic form of a while statement that loops over a sentinel variable :

// initialize the sentinel variable

while ( /* condition on variable is true */ ) {
  // take action
  // update sentinel variable
}

For example:

let x = 0;

while (x < 10) {
  console.log(x);
  x++;
}

We can use a loop of the same form to loop over all the elements in a collection.

❯ let allPs = document.querySelectorAll('p');
❯ let i = 0;
❯ while (i < allPs.length) {
    console.log(allPs[i].innerText);
    i++;
  }
[Log] I'm a little teapot.
[Log] Short and stout.
[Log] Here is my handle.
[Log] And here is my spout.

Notice the pattern: The sentinel variable is the variable we use to access items in the collection. It starts at 0, the location of the first element. The loop stops when the sentinel variable equals the size of the collection, because that value is past the last element in the collection.

We can also use a loop of this sort to modify all the elements in a collection. This loop adds a line number marker to the front of each paragraph in the poem:

❯ i = 0;
❯ while (i < allPs.length) {
    let element = allPs[i];
    let text = element.innerText;
    let lineNumStr = '[line ' + i + '] ';
    element.innerText = lineNumStr + text;
    i++;
  }

Note, we have to reset i to 0, because our previous loop advanced it to 4 before stopping the loop!

Finally, suppose that we would like to add a blue background to every element. We can use a loop to add the blue class to each element's class list:

❯ i = 0;
❯ while (i < allPs.length) {
    let element = allPs[i];
    element.classList.add('blue');
    i++;
  }

After writing so many while statements to process this list of elements, you may be wishing for a way to write the loop that is not so prone to error. JavaScript has such a statement, the for statement, which we will learn about in class this week.

If we are willing to put the action we want into a function, we can also use the forEach() method of the array:

❯ function addBlue(element) {
    element.classList.add('blue');
  }
❯ allPs.forEach(addBlue);

This can be the handiest of ways to process every element in a collection once you are comfortable writing JavaScript functions.

JavaScript Strings

Don't worry about mastering all the details of this section. It is intended as a brief introduction that we can return to later when we work with strings. Do note how the string acts like an array of characters.

We have been using JavaScript strings for a few weeks. Now that we know about how collections work in JavaScript, there are a few more features of strings that we can take advantage of — because strings are collections, too. They contain characters.

We can access the individual characters in a string using the square brackets:

❯ let name = 'Eugene';
❯ name[3]
"e"

Note that characters in JavaScript are strings of size 1.

Strings have a number of methods that are useful for working with text of elements in the DOM. Let's use this string:

❯ let sentence = 'The south wall warms me: November has begun,';
❯ sentence
"The south wall warms me: November has begun,"

... to consider some of them:


Finally, there is a connection between strings and arrays. We can split a string at a separator using the split() method. It returns an array of substrings that lie between the separators. This may be easiest to understand by example:

❯ let data = 'Wallingford,Eugene,EBAR 022'
❯ let parts = data.split(',');
❯ parts
["Wallingford", "Eugene", "EBAR 022"]

Arrays have a method that reverses this process: join(str) creates a string by combining strings for all of the array's elements, separated by str. For example:

❯ parts[2] = 'EBAR 019';
"EBAR 019"
❯ parts.join(',')
"Wallingford,Eugene,EBAR 019" 

We can use any characters to split strings or join arrays. This can be very handy for processing spreadsheet files that we would like to turn into rows in a table or entries in a list.

Closing Credits

The string used as an example in the preceding section is a line from the poem There's Nothing Like the Sun by Edward Thomas.