Digital humanities


Maintained by: David J. Birnbaum (djbpitt@gmail.com) [Creative Commons BY-NC-SA 3.0 Unported License] Last modified: 2015-12-01T00:53:52+0000


JavaScript piece by piece

Before you begin: linking your JavaScript

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 three basic components of a JavaScript program

The JavaScript file itself typically has three components:

  1. 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.

  2. 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).

  3. 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.

Sample project

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.

Sample HTML

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:


Obama Said to Plan Moves to Shield 5 Million Immigrants

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.

The JavaScript

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.

Bootstrapping with 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);

The original background color

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.

Initialization with 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.

The toggleHighlight() function

The 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

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);