Maintained by: David J. Birnbaum (djbpitt@gmail.com) Last modified: 2021-12-30T02:53:26+0000
Projects in this course (and in our own work) save JavaScript code in a separate file,
apart from the HTML, and then associate that file with the HTML with a special element
inside the HTML <head>
. This is similar to the
way we manage CSS: we create CSS in a separate file and associate it with the HTML
document by using a special linking element inside the HTML
<head>
. To link from your HTML to a JavaScript
file in the same directory called project.js
, add the following element to the
<head>
of your HTML page:
<script type="text/javascript" src="project.js">/**/</script>
In the section immediately below we summarize the three basic components of a JavaScript program. After that summary we provide examples of the code for each of the sections. Finally, we show a complete JavaScript program that we build from those components.
The JavaScript file itself typically has three components:
The function that does the work. There has to be a
JavaScript function that does something interesting with your document. The
something interesting
might be highlighting some text, making
something appear and disappear, etc.
A function that binds the target to the event
handler that invokes the function. We usually call this
function init()
, but the name is
arbitrary. The JavaScript function does what it’s supposed to do only when
something happens that causes it to execute. The function that does the
work
in #1, above, by itself, doesn’t know when to fire, that is, when
to turn highlighting on or off or toggle the visibility of something. You tell
it when to do that by creating an event handler and
binding the function from step #1 to the
target. The target is the part of the page with which you
interact, which in this case is the name that you click on. The event
handler is JavaScript code that gets bound to (associated with) the
target (here, a name on the page), so that the function in step #1 will fire
when a particular event (such as a click) happens on the target (such as a
name).
Initialization. The function in step #1 doesn’t fire unless an event happens to make it fire, and in step #2 we told the targets to listen for the event and invoke the function when the event happens. But the JavaScript code that does the binding is itself a function, and it isn’t enough just to write it and let it sit around in our script—we also have to make it fire. We do that by instructing the initialization routine (the one that binds the function we care about to the targets with which we’re going to interact) to fire when the page loads.
The flow, then, is that the user loads the HTML page and step #3 listens for when the page has finished loading and then fires step #2. Step #2 identifies the targets in the HTML with which we want to be able to interact and binds the function in Step #1 to those targets. That way, when the event we care about happens, the function will fire. Steps #3 and #2 fire only once; #3 fires automatically on loading and invokes #2. Step #1 fires every time the event for which we’re watching happens on a target.
There are other ways to construct and initialize your JavaScript, and the method described above has some limitations that can cause problems if, for example, you attach multiple JavaScript files to your HTML. The method outlined above is easy to understand because it separates the components neatly: 1) create a function that will do something you care about; 2) create code that will cause bits of your HTML (targets) to fire the function when you interact with them; and 3) cause the initialization code, which binds the function to the targets, to run when you load the page. If your needs are more complex, let us know and we’ll tell you about the risks of this simplified procedure and how to avoid them.
Highlighting is the easiest type of JavaScript action because it can involve just changing the background color of part of the page. This is simpler than other common project tasks, such as show/hide or popups, so we’ll create a page that will highlight parts of speech on demand. You can find examples of typical JavaScript activities used in course projects in the JavaScript section of our main course page.
Here’s a sample HTML document, taken from the lead paragraph of a story in the 2014-11-13 New York times:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Sample</title>
<link rel="stylesheet" type="text/css" href="project.css" />
<script type="text/javascript" src="project.js">//</script>
</head>
<body>
<ul>
<li><input type="checkbox" value="adj" />Adjective</li>
<li><input type="checkbox" value="art" />Article</li>
<li><input type="checkbox" value="conj" />Conjunction</li>
<li><input type="checkbox" value="noun" />Noun</li>
<li><input type="checkbox" value="number" />Number</li>
<li><input type="checkbox" value="prep" />Preposition</li>
<li><input type="checkbox" value="pron" />Pronoun</li>
<li><input type="checkbox" value="verb" />Verb</li>
</ul>
<hr />
<h1><span class="noun">Obama</span>
<span class="verb">Said</span>
<span class="verb">to Plan</span>
<span class="noun">Moves</span>
<span class="verb">to Shield</span>
<span class="number">5 Million</span>
<span class="noun">Immigrants</span></h1>
<p><span class="noun">President</span>
<span class="noun">Obama</span>
<span class="verb">will ignore</span>
<span class="adj">angry</span>
<span class="noun">protests</span>
<span class="prep">from</span>
<span class="noun">Republicans</span>
<span class="conj">and</span>
<span class="verb">announce</span>
<span class="conj">as soon as</span>
<span class="adj">next</span>
<span class="noun">week</span>
<span class="art">a</span>
<span class="adj">broad</span>
<span class="noun">overhaul</span>
<span class="prep">of</span>
<span class="art">the</span>
<span class="noun">nation’s</span>
<span class="noun">immigration</span>
<span class="noun">enforcement</span>
<span class="noun">system</span>
<span class="conj">that</span>
<span class="verb">will protect</span>
<span class="prep">up to</span>
<span class="number">five million</span>
<span class="adj">undocumented</span>
<span class="noun">immigrants</span>
<span class="prep">from</span>
<span class="art">the</span>
<span class="noun">threat</span>
<span class="prep">of</span>
<span class="noun">deportation</span>
<span class="prep">and</span>
<span class="verb">provide</span>
<span class="pron">many</span>
<span class="prep">of</span>
<span class="pron">them</span>
<span class="prep">with</span>
<span class="noun">work</span>
<span class="noun">permits</span>,
<span class="prep">according to</span>
<span class="noun">administration</span>
<span class="noun">officials</span>
<span class="pron">who</span>
<span class="verb">have</span>
<span class="adj">direct</span>
<span class="noun">knowledge</span>
<span class="prep">of</span>
<span class="art">the</span>
<span class="noun">plan</span>.</p>
</body>
</html>
We’ve tagged each word or group of words as belonging to one of eight parts of speech
(adj[ective], art[icle], conj[unction], noun, number, prep[osition], pron[oun], verb) by
tagging each word or group as an HTML <span>
element and marking the part of speech with the
@class
attribute. There’s no styling going on (yet)
and no use of JavaScript (except for the fact that we link to an external JavaScript
file with a <script>
element inside the
<head>
), so our markup has no effect on the
appearance:
President Obama will ignore angry protests from Republicans and announce as soon as next week a broad overhaul of the nation’s immigration enforcement system that will protect up to five million undocumented immigrants from the threat of deportation and provide many of them with work permits, according to administration officials who have direct knowledge of the plan.
We want to highlight parts of speech when the user checks a box and remove the highlighting when the user unchecks the box. This means that we need to create functions that 1) respond to the checking and unchecking by toggling highlighting, 2) bind event listeners to the checkboxes so that they’ll respond to a change in checked status, and 3) initialize the script so that the binding happens when the page loads. The complete JavaScript file is below, but we discuss the parts separately first.
window.addEventListener()
Our initialization is executed by firing the code below. Here, we declare a function
called init()
earlier in our code and pass its
name as an argument when we add the event listener (that is, when the event happens, we
specify that this is the function that should fire). In this case, the event is
window.DOMContentLoaded
, that is, the window
reports that the content of the page has finished loading. The function that will fire
when that event happens is init()
, which we
have to define (see below). The third parameter to
addEventListener()
should normally be the
keyword false
, which you can think of as just a magic incantation.
window.addEventListener('DOMContentLoaded', init, false);
We want to change the background color for each part of speech, and in order to be able
to change it back, we need to remember the original background color of the page. We can
retrieve the original background color and set it to a variable called
originalBackground
with:
originalBackground = document.body.style.backgroundColor;
We need to do this inside our init()
function
because we can’t determine the value safely until the page has loaded completely, so
that the <body>
element has been constructed and
the background color has been set. We declare the variable at the beginning of our
JavaScript with:
var originalBackground;
but we wait and assign a value to it only inside the
init()
function.
init()
Our initialization function, which we’ve called
init()
, reads as follows:
function init() {
originalBackground = document.body.style.backgroundColor;
var checkboxes = document.getElementsByTagName('input');
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('click', toggleHighlight, false);
}
}
This function retrieves the original background color of the page (actually, of the
<body>
element) and assigns it to a variable
called originalBackground
, which we initialized
(created) at the beginning of the script. As noted above, we can’t assign that variable
a value when we first create it because we have to wait until the page has loaded fully,
which avoids the risk of trying to retrieve the background color of the
<body>
before the CSS has loaded, and perhaps
even before the <body>
element itself has been
created.
The only <input>
elements in our document are the
checkboxes, so we can round them all up and assign them to a variable we’ll call
checkboxes
by getting all elements that have
the tag name input
, and we do this with
var checkboxes = document.getElementsByTagName('input');
We then loop over the items in our checkboxes
variable and attach an event listener to each one that says that when a click event
happens on one of those elements (that is, whenever a user clicks a checkbox), the
toggleHighlight()
function should fire.
The syntax for our loop is:
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('click', toggleHighlight, false);
}
The looping expression starts with the keyword
for
and then parentheses that contain three
semicolon-delimited components. In the first component, we declare a range variable,
which we arbitrarily call i
, and initialize it
to zero. In the third component, which reads
i++
, we say, using a JavaScript idiom, that
each time the loop finishes we should increment the value of our
i
variable. This means that the variable
i
will have the value 0
the first time
through the loop, then it will be equal to 1
, then 2
, etc. So when does
the looping stop? That’s the job of the middle component, which compares the value of
i
on each pass through the loop to the number
of checkboxes (in JavaScript, we can find out how many items we have in the variable
checkboxes
by using the
.length
property expression, that is, by
tagging a period and the word length
after the object whose components we want to
count). Since JavaScript starts counting from 0, the first item in our group of 8 items
in checkboxes
is item #0, the last is #7, and
we want to keep looping as long as the value of
i
is less than the count of items, that is,
less than 8. The loop terminates whenever the test in the second component of the
for
statement is false.
The syntax for adding an event listener is the same as for adding a listener to the
DOMContentLoaded
event. We add
.addEventListener()
after the element to which
we want to attach the listener, and we use the value of our
i
range variable to address those elements in
turn. The first one will be checkboxes[0]
, the
next one checkboxes[1]
, etc., so if we refer to
checkboxes[i]
inside the loop and the value of
i
starts at 0 and we stop looping after we’ve
finished item #7, that means that checkboxes[i]
will refer to each of the checkboxes in turn, until it runs out of checkboxes.
The .addEventListener()
method (a method here is something we do to an object) takes three arguments:
the first is the name of the event (in quotation marks), the second is the name of the
function to fire when the event occurs (without the parentheses), and the third is the
word false
(without quotation marks; for more information about false
vs
true
see Ilya Kantor’s Bubbling and
capturing tutorial, but the short version is that this value should almost
always be set to false
). This means that if we set the value of a variable
i
to a number inside a loop (see above), the
expression
checkboxes[i].addEventListener('click', toggleHighlight, false)
;
will take the i’th member of our group of checkboxes and assign to it an event listener
that will cause it to fire the
toggleHighlight()
function whenever the user
clicks that particular checkbox. Remember: the name of the event must be in
quotation marks (single or double; you can find a list of all possible events on Dottoro), the name of the function
must not include the parentheses, and the name of the function and the word
false
must not be in quotation marks.
toggleHighlight()
functionThe toggleHighlight()
function fires whenever
the user clicks on a checkbox. The function first gets the part of speech and assigns it
to a variable pos
, using:
var pos = this.getAttribute('value');
You can refer to the target, the place where the event happened, with the
special word this
, and we use the .getAttribute()
method of the target to get the value of its
@value
attribute. We cleverly used the same strings
for the @value
attribute of the checkbox and the
@class
attribute of the
<span>
elements that we wrapped around each
word, so we can use the pos
variable we are
creating as a way of finding the elements whose background color we want to change.
To choose the appropriate color, we initialize a variable
color
and then give it a value using the
JavaScript switch()
structure, which you can
read about at W3Schools:
var color;
switch (pos) {
case 'adj':
color = 'yellow';
break;
case 'art':
color = 'orange';
break;
case 'conj':
color = 'teal';
break;
case 'noun':
color = 'red';
break;
case 'number':
color = 'sandybrown';
break;
case 'prep':
color = 'lightgreen';
break;
case 'pron':
color = 'violet';
break;
case 'verb':
color = 'lightblue';
break;
}
The short version is that switch()
compares the
value of its argument (the pos
inside the parentheses) to each of the values
after the case
statements, and when it finds a
match, it executes all of the statements until the first
break
statment. For example, if the part of
speech is conj
, the switch()
structure
will have the effect of setting the variable called
color
to the value teal
.
We next set a variable status
, which records
whether the checkbox is checked or not:
var status = this.checked;
// the status to which you've just changed the checkbox
The expression this.checked
returns the checked
property of the target, the checkbox we clicked, and the value is guaranteed
to be a Boolean value true
or false
. Note that the status that is returned
is the one we’ve just set by clicking, and that’s because the function fires only in
response to (after) our click event. The value isn’t what you saw before you clicked,
but what you made happen by clicking.
We next assign to the variable spans
all of the
<span>
elements we want to toggle, that is, all
of the ones with a @class
attribute that matches
the pos
variable
var spans = document.getElementsByClassName(pos);
We find those by using the expressions
document.getElementsByClassName(pos)
, which
gets all of the elements in the document (of any type, but we know that they are all
<span>
elements) that have a
@class
value that matches the value of our
pos
variable. We then loop over the items in
spans
and for each one we check whether the
value of status
is true
:
for (var i = 0; i < spans.length; i++) {
if (status == true) {
spans[i].style.backgroundColor = color;
} else {
spans[i].style.backgroundColor = originalBackground;
}
}
If it is true, that means that we’ve just checked the box, so we set the background color
of the <span>
to the color we chose inside the
switch
expression. If the value of the
status
variable is false, we’ve just unchecked
the box, so we set the background color of those elements back to the original
background color for the page.
Beware! In JavaScript, a single equal sign assigns a value to a variable; it
does not check whether two values are equal. In our
if
statement above, we used a double equal
sign, which is how we test for equality. The trap is that if you use a single equal
sign where you really want a double one in the
for
statement above, you won’t get an
error. You’ll assign the Boolean value true
to the variable
status
, that assignment will always
succeed, so the test
will always pass and you’ll always assign a non-original
background color (that is, you’ll be unable to toggle off the highlighting). Try
changing the double equal sign to a single one to see the damage!
Challenge question: This isn’t the most efficient way to change the colors because
we’re checking the value of the status
variable inside the loop, that is, once for each item in our group of
spans
, even though it will always have the
same value. How would you tighten up the code to avoid that repetition?
The complete JavaScript file is below and a working example (HTML with links to CSS and JavaScript) is at http://dh.obdurodon.org/javascript/pos/nyt_basic-with-spans.xhtml
var originalBackground;
function toggleHighlight() {
var pos = this.getAttribute('value');
var color;
switch (pos) {
case 'adj':
color = 'yellow';
break;
case 'art':
color = 'orange';
break;
case 'conj':
color = 'teal';
break;
case 'noun':
color = 'red';
break;
case 'number':
color = 'sandybrown';
break;
case 'prep':
color = 'lightgreen';
break;
case 'pron':
color = 'violet';
break;
case 'verb':
color = 'lightblue';
break;
}
var status = this.checked;
// the status to which you've just changed the checkbox
var spans = document.getElementsByClassName(pos);
for (var i = 0; i < spans.length; i++) {
if (status == true) {
spans[i].style.backgroundColor = color;
} else {
spans[i].style.backgroundColor = originalBackground;
}
}
}
function init() {
originalBackground = document.body.style.backgroundColor;
var checkboxes = document.getElementsByTagName('input');
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('click', toggleHighlight, false);
}
}
window.addEventListener('DOMContentLoaded', init, false);