Author: Elisa Beshero-Bondar (ebb8@pitt.edu) Maintained by: David J. Birnbaum (djbpitt@gmail.com) Last modified: 2021-12-27T22:03:49+0000
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.
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).
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.
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.
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.
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.