Session 17: Changing Text on a Page with JavaScript Functions

Download this starter code to use for the opening exercise and the rest of the session.

Opening Exercise: Change Text on a Web Page

Last time, we learned how to write JavaScript code to change the content of a web page. Let's refresh our memory by changing the content of this page.

Write a script that changes the h2 heading to "2024 is in the books. See you in 2025!".

Bonus challenge: if you have read this week's reading, add code to change the color of the new heading to red.

You may put your code in a script element at the bottom of the web page, or in the file set-text.js, which is loaded by the web page.

Solution

Here is a possible solution. The key is to use querySelector() and innerText:

let element = document.querySelector('h2');
element.innerText = "2024 is in the books. See you in 2025!";

If we want to make the heading smaller, we can use the element's innerHTML property:

element.innerHTML = "<h5>2024 is in the books. See you in 2025!</h5>";

To change the color of text, we access the element's style attribute:

element.style.color = "red";

Recall that the DOM reveals an element's styling through the data property style, which is itself a JavaScript object that contains all of the style properties as name/value pairs. We write styling information differently in JavaScript than in CSS, because we have to use JavaScript strings and naming conventions.

Back to St. Ives

Last time, we learned about JavaScript objects, which contain data (known as properties) and methods. We then saw learned that our web browser exposes a web page to us as a JavaScript object, according to the domain object model (DOM). Together, we used these ideas to write a JavaScript script to change the content of a web page.

As we noted at the end of Session 16, all is good with our code until someone changes the story to include groups of 9 wives, sacks, cats, and kittens — or 11 or 5. Our code repeats the number 7 is several lines of code, and now we would have to change each one of them to the new number.

We can do better: we can write a function. Knowing how to write functions will be an essential skill for writing many of the scripts we want to be able to write. Let's learn about JavaScript now.

Functions

A function is

* We'll expand on this later. Indeed, all three elements of this definition are subject to elaboration.

We have already seen a kind of function in the form of methods associated with JavaScript's Math, String, and console objects.

We can define and call our own functions, too.

Here is the definition of a function to say 'hello' to the user of a web page:

function sayHello() {
  alert('Hello!');
}

alert() is a built-in function like prompt(), which is introduced in Homework 7. alert() is useful for showing the user information that arises in the course of an interaction.

We can execute code like this in the console, just like any other JavaScript. Once we have defined the function, we can use iy to execute the code it contains:

sayHello();

We often want to give some information to a function to help it do its job. Here is definition of a function that takes an argument:

function sayHello(name) {
  alert('Hello, ' + name + '!');
}

sayHello('Alice');

When we give 'Alice' to the function, it assigns that value to the variable name before executing the function's code.

We also frequently want a function to give us back an answer that it has computed. Here is definition of a function that returns a value:

function sayItTwice(somethingToSay) {
  return somethingToSay + ' ' + somethingToSay;
}

let message = sayItTwice('I have a lot to say!');
alert(message);

When a function returns a result, we can use that result in whatever way is useful to us. Here, I assigned the value to the variable message so that I could use that variable in another expression.

let message = sayItTwice('Rochelle');
sayHello(message);
Write a function named makeURL(university).

The function receives a string as an argument. The string is a university's domain name.

The function returns the URL for university's website. For example, UNI's domain is 'uni', so makeURL('uni') should return "https://uni.edu".

Call your function with "uni" but also with a couple of other university's domains, such as "uiowa" and "iastate".

A Function for St. Ives

We can use these ideas to improve our script for the St. Ives page. First, we can group the calculations into a function that gives a name to the action as a whole:

function countTravelers() {
  let wives = 7;
  let sacks = 7 * wives;
  let cats = 7 * sacks;
  let kittens = 7 * cats;
  return wives + sacks + cats + kittens;
}

Then we call the function instead of hardcoding the answer:

target.innerText = currentText + " " + countTravelers();

Now we can make our code work for other numbers of travelers. If the number of wives-sacks-cats-and-kittens can change, or if we want to give the user control over the number, then we can modify our function to take the number of wives et al. as an argument:

function countTravelers(count) {
  let wives = count;
  let sacks = count * wives;
  let cats = count * sacks;
  let kittens = count * cats;
  return wives + sacks + cats + kittens;
}
...
target.innerText = currentText + " " + countTravelers(7);

The result is this JavaScript file. Try it with 8 or 9 or 42. The answer updates correctly!

Functions will be very helpful to us as we write scripts. They will be essential as we learn how to interact with users using buttons, forms, and other input elements. They will be helpful as we write code that can be reused in different settings.

Exercise: A Function to Change Any Element's Text

Writing a function gives us the ability to use a group of statements repeatedly without having to duplicate the code. Let's put that power to use.

Write a function named setText() that:
  • takes two arguments: a CSS selector and a string,
  • finds the element using the given selector, and
  • changes its text content to the given string.
Put your function in set-text.js and test it on the web page from our opening exercise, which loads the script file.

This function needs two pieces of information to do its job. A function can receive multiple arguments by separating their names with a comma:

function setText(selector, newText) {}

When we call the function, we also separate the arguments with a comma:

setText('p', 'REDACTED')

A Solution

Here is a possible solution.

function setText(selector, newText) {
  // find the HTML element
  let element = document.querySelector(selector);

  // change its text
  element.innerText = newText;
}

/*
 * test the function
 */
setText('p', 'REDACTED');

This function uses its first argument to select the HTML element:

function setText(selector, newText) {
  // find the HTML element
  let element = document.querySelector(selector);

  // change its text
  element.innerText = newText;
}

/*
 * test the function
 */
setText('p', 'REDACTED');

It uses its second argument as the new text for that element:

function setText(selector, newText) {
  // find the HTML element
  let element = document.querySelector(selector);

  // change its text
  element.innerText = newText;
}

/*
 * test the function
 */
setText('p', 'REDACTED');

When call the functions, the values we give it are assigned to those variables and used in the code.

function setText(selector, newText) {
  // find the HTML element
  let element = document.querySelector(selector);

  // change its text
  element.innerText = newText;
}

/*
 * test the function
 */
setText('th', 'Location');

With practice, this idea of taking code that we want to use, generalizing it to work for different values, and putting that code into a function will become more familiar.

A Comment on Comments

Recall that we learned about JavaScript comments last time. The JavaScript engine in the browser ignores comments. Their purpose is to make our code more understandable.

Comments can be

// one-line comments

in which case all characters to the end of the line are ignored. They can also be

/* they can
  span many
  lines */

as in CSS.

Use comments sparingly to explain your code. Whenever possible, use good variable names — then the code will explain itself.

Improvement #1: Ask the User for a Number

There are two weaknesses in nursery rhyme page at this point.

First, we hardcode a 7 (or 9, or 42) as a "magic number" in the call to countTravelers(). Why not let the user tell us which number to use?

We can be accomplish using prompt(), which is introduced in Homework 7. prompt() prompts the user, lets the user enter a value, and returns the answer as a string. We need a number for our calculation, so we ask JavaScript's Number object to convert it to a number before passing it to the function:

// Ask the user to enter a number and save the answer
let usersChoice = prompt("How many wives are there?");

// Ask the Number class to convert usersChoice to a number
let numberOfWives = Number(usersChoice);

// Use the number as before
target.innerText = currentText + " " + countTravelers(numberOfWives);

The prompt() function is not an elegant way to interact with the user. In the coming weeks, we will learn other HTML and JavaScript tools we can use to seek user input.

Improvement #2: Change the Text to Match the Number

Our prompt lets the user control the number used in the rhyme, but now the text is incorrect! Even if the user enters a number different than 7, the text of the rhyme still says 7. Why not have our script modify the text to match the number?

I anticipated this task when I wrote the HTML for the web page. Notice that all of the 7s in the text of the rhyme are wrapped in a <span class="times"></span> element. This gives us a "hook" for selecting the 7s and acting on the text.

This creates a new challenge for us, though. We know how to use querySelector(".times") to select the first of the 7s and set its inner text to the string the user entered:

// change the text to match the user's number
target = document.querySelector(".times");
target.innerText = usersChoice;

But there are four kinds of item to count, so 7 occurs four times in the text. How can we access the other three?

We could give a different class name to each occurrence and target each separately. That would require us to repeat the same two lines of code four times in order to select four different classes. We could try CSS's :nth-of-type() selector, but that, too, would require us to repeat our text-changing code four times, with minor tweaks.

JavaScript offers a better way. That path starts with a new query selector:

document.querySelectorAll('.times')

This method returns a new kind of object: a list containing all of the elements that match the query. JavaScript includes tools for working with objects of this sort, in particular for writing code that says "Do this action for each item in the list:". They require us to write... a function!

Working with lists is a topic for another day, but wouldn't it be nice to modify our script to do the job? Here is the code we need:

function changeText(e) {
  e.innerText = usersChoice;
}

// change the text to match the user's number
targets = document.querySelectorAll(".times");
targets.forEach(changeText);

The result is this JavaScript file — and a fun nursery rhyme web page.

Now we know one thing that we will want to study in the coming days. This will be one of our learning goals for the coming week. Until then, though, let's stick with querySelector() and practice working with single elements of the DOM.

Closing

Reading 9

Homework 7