Week 10: More JavaScript, Including if
Statements
Table of Contents
We dive deeper into JavaScript this week, revisiting two topics and introducing a new topic. The links in the following list are to sections on this page.
-
review
the
nth-child()
selector in more detail -
learn about
if()
statements - learn about Boolean expressions
- review how values are passed to and from functions
Download this starter code to use throughout the reading.
The nth-child()
Pseudo Class
Often we don't have the luxury to select elements by their type,
class, or ID, especially when we write JavaScript programs to
manipulate web page content. There are many powerful CSS
selectors that can help us in these situations, and one of them
is
the nth-child()
pseudo-class.
We encountered this selector briefly in
Session 11
and
Session 13.
Let's learn more about it now.
Consider this web page with a table styled by this CSS file.
This selector matches a number of child elements whose numeric position in the series of children matches the argument. We can use a number there, such as 3, or a keyword, such as 'even'.
We can write more complex arguments to nth-child()
.
This selector matches a number of child elements whose numeric
position in the series of children matches the pattern
an + b
, or just n
when a=1 and b=0.
For example, you could use it to select every fifth row in a
table, every third cell in a row, or every 42nd paragraph on a
page.
See the MDN documentation for more details.
We can also use this pseudo-class as a selector in our
JavaScript. The querySelector()
method returns
only the first occurrence of an element that matches
the selector. Often that is exactly what we need in a script.
Consider the table. Ames is the third row of the table. Its population is second item in the row. How might we change the color of that number red?
How do we select the third row in the table?
tr:nth-child(3)
.
How do we select the second table cell?
td:nth-child(2)
.
Can we select the second table cell in the third row directly? Yes. At times this semester, we have seen that CSS can select an element that is inside another element by combining the selectors with a space. For example, we can select all the paragraphs in a section with:
section p { ... }
This is called a descendant selector, which has some other useful features.
We can use descendant selectors in our JavaScript, too. So, to select the second cell in the third row of a table directly, we can do this:
> document.querySelector('tr:nth-child(3)') // third row <tr> <td>Ames</td> <td>67,282</td> <td>8</td> </tr> > document.querySelector('tr:nth-child(3) td:nth-child(2)') // second cell in third row <td>67,282</td> > let item = document.querySelector('tr:nth-child(3) td:nth-child(2)'); > item.style.color = 'purple';
nth-child()
and descendant selectors can both be
very handy in our CSS and in our JavaScript.
Practice Exercise: nth-child()
Open the script file nth-child.js in VS Code and:
- prompts the user for a column number,
- prompts the user for a color,
-
finds the
td
cell in the user's column, and - changes its backgound color to the user's color.
Test your script on the university cities page, which loads the script file.
This script will have to build a string for the selector. For
example, if the user enters '3' for the column number, the script
will construct: 'td:nth-child(3)'
.
You can build the string with the +
concatenation
operator, using two literal strings and the user's entry.
A Solution, and a New Goal
Our script needs to follows the process outlined in the spec:
- Prompt the user for a column number.
- Prompts the user for a color.
- Find the
td
cell in the user's column. - Set the element's backgound color to the user's color.
In this case, we can write three of those steps with one statement of JavaScript each. The third step might be easier if break it down into two steps:
- Prompt the user for a column number.
- Prompts the user for a color.
-
Find the
td
cell in the user's column:- Build the selector
- Query the document for the matching element.
- Set the element's backgound color to the user's color.
Now the third step seems approachable, too. Here is a possible solution in JavaScript:
let column = prompt('Column number?'); let color = prompt('Color?'); // Find the td cell in the user's column let selector = 'td:nth-child(' + column + ')'; let cell = document.querySelector(selector); cell.style.backgroundColor = color;
Try it with '3' and 'lightblue', or '1' and 'green'. All works
well. The argument to document.querySelector()
is
a string, which can build as easily as we can type. This gives
us a lot of freedom to create interactions.
Now try it with '4' and 'lightblue'. What happens?
Nothing happens on the web page, because there isn't a fourth column in the table. If we open the developer tools, we see that there was an error while executing the script. The
TypeError: null is not an object (evaluating 'cell.style')
on Line 5 of the file. Click on the line number, and the tools
take us to the offending line. The error actually occurred on
Line 4, when document.querySelector(selector)
returned null, because td:nth-child(4)
is not a
valid selector in a table of three columns.
Wouldn't it be nice if we alerted the user to the error, rather making them guess, or troll around in the developer tools? To do so, the script needs to validate the column number before proceeding with the rest of its steps:
- Prompt the user for a column number.
- Check if number is a valid selection.
-
If valid:
- Prompts the user for a color.
-
Find the
td
cell in the user's column:- Build the selector
- Query the document for the matching element.
- Set the element's backgound color to the user's color.
Let's learn the JavaScript we need to make decisions such as "Is this number valid?".
The if
Statement
In JavaScript, we make decisions with an if
statement. The simplest form of if
statement
looks like this:
if (expression) { // execute these statements [A] } // resume flow and execute these statements [B]
An if
statement works like this:
- If expression evaluates to true, the browser will execute the statements at [A] and and then execute the statements at [B].
- However, if expression evaluates to false, the browser will skip the statements at [A] and execute only the statements at [B].
For example:
if (x > 1) { console.log('greater'); } console.log('moving on');
Try this code with x = 3 and x = -1 and x = 1.
We can use any expression that evaluates to true or false as the test expression. We will explore test expressions later in the reading.
The if-else
Statement
We can also make either/or decisions with an if
statement by adding an else
clause:
if (expression) { // execute these statements [A] } else { // execute these statements [B] } // resume flow and execute these statements [C]
An if-else
statement works like this:
- If expression evaluates to true, the browser will execute the statements at [A] and and then execute the statements at [C].
- However, if expression evaluates to false, the browser will skip the statements at [B] and execute only the statements at [B].
For example:
if (x > 1) { console.log('greater'); } else { console.log('not big enough'); } console.log('moving on');
Try this code with x = 3 and x = -1 and x = 1.
With an if-else
statement, either the 'then' part is
executed or the 'else' part is executed. One of the options will
always be executed.
An if-else
statement is just what we need to implement
the new logic for the script to solve
the opening exercise:
- Prompt the user for a column number.
- Check if number is a valid selection.
-
If valid:
- Prompts the user for a color.
-
Find the
td
cell in the user's column:- Build the selector
- Query the document for the matching element.
- Set the element's backgound color to the user's color.
- Otherwise: alert the user.
We can ensure that the user does not enter a column number that is too large. Here is the new code:
let column = prompt('Column number?'); if (column < 4) { let color = prompt('Color?'); let selector = 'td:nth-child(' + column + ')'; let cell = document.querySelector(selector); cell.style.backgroundColor = color; } else { alert('The column number must be 1 through 3.') }
Try it out. Our code now checks to see if the column number is too big.
But what if the user enters a 0, or a negative number? We need a way to write a different kind of boolean expression that combines two expressions. Let's learn that next.
Boolean Expressions
The test expression for an if
statement must
evaluate to true
or false
. This kind
of expression is called a boolean expression,
after the 19th-century mathematician George Boole, who codifie
many rules that govern true/false expressions.
The test expression can be:
-
a boolean value:
true
orfalse
(notice: no quotation marks) - a variable holding a boolean value
- an expression that evaluates to a boolean value
One way to write a boolean expression is to
compare two expressions to get a true/false
value.
a < b
evaluates to true
if a
is less than
b
, and false otherwise.
These are the common comparison operations:
>
— greater than<
— less than===
— equal to>=
— greater than or equal to<=
— less than or equal to!==
— not equal to
These operators compare values of any type (say, numbers or
strings) and give a boolean value. For example,
5 > 3
evaluates to true, 5 < 3
evaluates to false, and 'Eugene' === 'Eugene'
evaluates to true.
JavaScript also provides operators that work with boolean values themselves. They are called logical expressions.
expression1 && expression2
means
"expression1 and expression2". It evaluates
to true if both expression1 is true
and expression2 is true.
expression1 || expression2
means
"expression1 or expression2". It evaluates
to true if either expression1 is true
or expression2 is true.
We can also negate a boolean expression using the !
operator.
! expression
means "not the value of expression".
It evaluates to true if the expression is false, and false if
it is true.
Finishing the Opening Exercise
The &&
logical operator is just what we
need to complete the nth-child exercise. To be a valid column
number for the cities table, the value must be
both greater than 0
and less than 4:
if ( (column > 0) && (column < 4) ) ...
Remember: the entire test expression must be enclosed in a pair of parentheses.
Try it one last time with several column numbers: 1, 3, 0, 4, -2, 12.
Success: the final code.
Perhaps, though, we would like to improve the script one more time... Rather than alerting the user that a column number is out of range, we could ask the user to try again. We will need to write a boolean expression to do this, but we also need a way in JavaScript to do something again. This will be one of our topics for the next session or two.
Passing Values to Functions
Now we switch to another topic altogether.
We have been writing functions now every day for several sessions, both so that we can call them and so that we can give them to elements in the browser, to be called in response to a click in the window. Let's make sure we understand how functions work.
Recall that a function is
- a named piece of code
- that groups statements together
- and makes code reusable.
We define a function using the function
keyword:
function showDistance(speed, time) { alert(speed*time); }
We call a function by using its name with a pair of parentheses:
showDistance(10, 5);
How do we know if a function requires us to give it some data
when we call it? We look at the function definition to see if
it lists any parameters. In the
showDistance
function,
speed
and
time
are the parameters. They tell us
that showDistance
requires two pieces of
information to do its job. Their names suggest that they are a
speed and an amount of time.
When we call the function, we pass two values to the function.
These values are called arguments. In the
example above, 10
and
5
are the arguments.

The arguments we pass can be any expression that produces a value:
-
values:
showDistance(10, 5);
-
variables holding values:
let mySpeed = 10; let myTime = 5; showDistance(mySpeed, myTime);
-
expressions that are evaluated to produce values:
showDistance(20 + 80, 10 / 2);
-
any combination of the above:
let mySpeed = 80; showDistance(mySpeed + 20, 5);
We use the names of the parameters in the function definition as variable names. These variables, though, exist only inside the body of the function. JavaScript doesn't care what names we give our parameters. We choose names that convey the meaning of the values, so that our code is easier to read and understand.
In the function, these values of the arguments are assigned to the names of the parameters, in essentially the same way we assign values to variables in our code.

Calling a function in this way implements the same process that would have happened if we simply wrote the code as a sequence of statements, without a function.