Digital humanities


Author: Elisa Beshero-Bondar (ebb8@pitt.edu) Maintained by: David J. Birnbaum (djbpitt@gmail.com) [Creative Commons BY-NC-SA 3.0 Unported License] Last modified: 2014-11-18T02:14:56+0000


JavaScript toggling with classList and switch

The problem

Let’s say you’ve tagged an XML file to identify people and places as well as to classify activities and specialized concepts. You may then want to present that complex array of information in HTML in a way that readers can interact with, so they can toggle checkboxes on a menu to highlight people or places or concepts, depending on their specific interests. For example, the Pacific team from Spring 2013 present their poems with a menu of clickable options to highlight or unhighlight spans of HTML. To do this, they transformed a variety of XML elements into <span class=""> HTML tags and used the value of the @class attribute to control the highlighting through JavaScript and CSS.

Overview

We will need to integrate JavaScript with CSS and HTML to manipulate class attributes, writing JavaScript to find HTML elements by class and add a new, additional class to them. We’ve called our new class on because we use it to toggle colored highlighting on, but you can use any name you want. In our JavaScript, we’ll add the class value on dynamically to <span> elements when we want to toggle on highlighting for them, and in our CSS we’ll use this new on class to assign the colors we want to use for that highlighting. We’ve used hexadecimal color values, but you can use any legal color notation you want (for nuanced color selections, I recommend the handy color picker at w3schools).

The CSS

Our places are already defined as <span class="place">, and when we want to highlight them in the display, we use JavaScript to change the value of the @class attribute to add the additional class on. We describe below how to change the value of the @class attribute, and once we’ve done that, the following CSS will respond to the new value. (We similarly set special, highlighted color values for characters and objects when they are toggled on):

span.place.on {color:#3333CC}
span.character.on {color:#FF3300}
span.object.on {color:#477519}

We don’t have to designate an off value or a new class called off because the JavaScript routine below just adds and removes on from the class values associated with the <span> in response to alternating mouse clicks. Removing the on value causes the CSS no longer to apply to those <span> elements, which revert to their normal styling automatically.

The JavaScript

Our JavaScript needs to add and remove the value on from designated <span> elements whenever a user toggles the corresponding checkbox or button. The easiest way to do this is with a property called classList, which is available in all modern browsers. (In addition to toggling, classList can also be used for listing, manipulating, adding, and removing @class values, about which see http://nostrongbeliefs.com/notes-on-the-classlist-api/ and http://davidwalsh.name/classlist). For our purposes, we’ll apply the method (a method is an action that can be performed on an object, where the object in this case is the @class attribute of the element you want to process) classList.toggle(), which is optimized to switch effects on and off in response to a particular user action, such as a mouse click in a checkbox.

We can use the same JavaScript function to toggle highlighting for places, people, and concepts. When we click on an element that is listening for mouse events in order to fire a toggle (such as a particular checkbox), the element that we clicked can pass into the toggle() function (which we write; see below) not only the fact that a click happened, but also the type of element it is supposed to toggle. That means that all of our checkboxes will invoke the same toggling function, but they’ll also tell the function to toggle only a particular type of element, the type that makes sense for that particular checkbox. For example, we can configure one checkbox so that when we click on it, it not only fires the JavaScript routine to toggle highlighting, but also specifies that it is the checkbox that toggles highlight for, say, places, but not people. For the Pacific team, this general strategy of passing the name of the target to the function means we don’t have to write twelve different JavaScript functions for the twelve types of objects susceptible to highlighting. Here’s our simple JavaScript code:

function init() {
    var fieldset = document.getElementsByTagName('input');
    for (var i = 0; i < fieldset.length; i++) {
        fieldset[i].addEventListener('click', toggle, false);
    }
}

function toggle() {
    var id = this.id;
    switch (id) {
        case "CHARtoggle": {
            var chars = document.getElementsByClassName("character");
            for (var i = 0; i < chars.length; i++) {
                chars[i].classList.toggle("on")
            }
        };
        break;
        case "PLtoggle": {
            var places = document.getElementsByClassName("place");
            for (var i = 0; i < places.length; i++) {
                places[i].classList.toggle("on")
            }
        };
        break;
        case "OBtoggle": {
            var objects = document.getElementsByClassName("object");
            for (var i = 0; i < objects.length; i++) {
                objects[i].classList.toggle("on")
            }
        };
        break;
    }
  }

window.onload = init;

The JavaScript is designed to respond when users click on a checkbox. In this document, the HTML <input> elements are all checkboxes, so in our init() function we attach an event listener to tell the checkboxes that the specified function (here toggle()) should fire whenever the specified event (here click) happens on the specified element (here <input>, that is, a checkbox). (You can think of the third argument to addEventListener, false, as a magic incantation. It has to be there, but you don’t have to worry about what it does.)

When users click in a checkbox, then, they fire the function toggle() in the JavaScript, which is written to respond to a defined range of special cases. Each checkbox has a distinct @id attribute in the HTML, so we set a JavaScript variable id (note that JavaScript variable names, unlike variable names in XML-related languages, don’t begin with a dollar sign) equal to the value of the HTML @id of whatever the user clicked. We then use the switch functionality of JavaScript to test the value of that id variable, comparing it to a series of values, each specified in a case statement. For example, when the user clicks on a checkbox input element identified as <input id="PLtoggle">, our function will find all elements with a @class value of place and toggle whether they also have a @class value of on. If they don’t, that additional @class value is associated with the element (toggled on). If it is already present, it is removed (toggled off). The CSS settings respond in real time to these changes; whether an element is highlighted is updated dynamically whenever the @class value on is added or removed.

The switch statement in JavaScript is written structurally as a series of case statements separated by break statements. switch works by testing each case in the order in which they’re listed, from first to last. You need the break statements because if they aren’t there, even if a case matches, the switch routine will continue testing whether other cases also match. In this example that would be a waste of processing, since we have no elements that are simultaneously characters and places and objects, so we use break to tell the function that it can break off there and not test any other case possibilities. This statement has a wide range of potential uses beyond our toggling example here, and you may want to try it with other kinds of functions, too, wherever you're working with multiple special cases that hinge on a particular kind of user interaction. Each special case that fires could have its own distinct display scenario.

.getElementsByClassName() returns an array of elements (an array in this case is essentially an ordered list) that have a particular, specified HTML @class value. Similarly, .getElementsByTagName() (note that both include the plural of elements) finds all elements that have a particular, specified tag name (element name). To do something to each of the returned elements in turn (in this case, to toggle their @class property), you have to iterate over them using the for loop. That’s true even if there is only one matching element; it’s still an array, and although an individual element has a classList property, an array of element(s) doesn’t.

Configuring the toggles in the HTML

Here’s a sample of the Pacific team’s HTML code for their clickable menu (markup not specifically required for the JavaScript toggle has been removed to simplify the presentation):

<div>
  <fieldset>
    <legend>Click to Highlight:</legend>
    <input type="checkbox" id="CHARtoggle"/>
    <span>Persons, Groups, and Mythic Entities</span>
    <input type="checkbox" id="PLtoggle"/>
    <span>Places</span>
    <input type="checkbox" id="OBtoggle"/>
    <span>Objects, Flora, and Fauna</span>
    <!-- Several more checkboxes -->
  </fieldset>
</div>

Here we see how the checkboxes are each given a distinct @id, which is (quite simply and elegantly) all that we need to trigger the specific case for highlighting a designated @class with our JavaScript toggle() function.

Older and more fragile approaches to toggling

Before the recent emergence of classList, writing JavaScript to toggle a change of display required a more complicated coding strategy, often involving string manipulation and the writing of regular expressions to find, remove, or add text to a list of @class values. To remove a class used to require finding its literal string value with a regular expression, and this could easily misfire when, for example, the letters in the attribute on were inside another class called onion or ontology. Writing regex to grope about for strings, and to split and join strings in a list of @class values, was a fragile approach at best.

It was also tempting in this context simply to find a class and change its color directly in JavaScript, without going to the trouble of adding and removing strings from the @class attribute. But this approach was (and is) fragile in its own way because browsers may not represent color internally the same way as it is set by the developer. There are several notations for encoding code color information, including RGB numbers (specifying the degree of red, green, and blue used to compose a color), hexadecimal values (a six-digit hexadecimal number), and simple words like red, blue, and green. In May 2013, when, instead of using the strategy described above we tried writing JavaScript code to change an element’s background color by setting a hexadecimal value for that color, we found that IE, Chrome, Safari and other browsers would change the color value correctly, but later were unable to recognize the color’s hexadecimal value to remove it, if set, when toggling off. This was because the browsers had quietly translated the lexical shape of our color values (the way we had spelled them out, in our case using hexadecimal values) to a lexical RGB value, and our attempts at string-matching hexadecimal values then failed because those were not the strings the browser had stored internally. We could have worked out the color encoding method that was in use by a particular browser, or just checked for them all, but that is a brittle solution, because we have no guarantee that browsers won’t change their preferred syntax for colors in a new release. This is why, in a nutshell, it’s best to store color encoding (in whatever designation works for you) in CSS styling of classes, and write JavaScript to add and remove those classes on toggling. Fortunately, the advent of the classList property has met a long-standing need for a natural, simple, and robust way to interact dynamically with the HTML @class attribute.