Handling Events with JavaScript
This is an action-packed chapter, where they introduce JavaScript, the DOM, events and event handlers all in one. We've spread that out over the last few class meetings, but now it's time to delve into what the chapter covered.
The main goal of the chapter is to add an event handler to the thumbnail otter pictures so that clicking on a thumbnail will cause that picture to be displayed in the larger version (called the detail image). This is a very cool and useful effect, often used in web pages with image galleries like this one.
If you don't have the book, here's a link to a copy of a scan of the chapter. To respect the author's copyright, the link is only valid on-campus or with a password. Ask Scott if you don't have that password.
Recap¶
I won't try to recap everything in this dense chapter, but I'll try to remind you of the main points
- we can add arbitrary information to a DOM element by adding
attributes. To make sure that the attribute doesn't conflict with one
that has meaning to the browser, we need to ensure that the first five
characters of the attribute name is
data-
. For example, if I want to add information to an element about how classy it is, I could add adata-class=fancy
attribute. - we can look up a DOM element using
document.querySelector()
where the argument is a string that contains a selector expression in the CSS language, such as#fred
or.gryffindor
or even[data-image-role="target"]
which looks up an element that has that attribute/value pair. - the JS objects returned by
querySelector
have properties that we can modify, thereby updating the page. For example,obj.src="otter1.jpg"
changes thesrc
of an element (presumably an IMG), which would cause the browser to load and display a different picture. - there is a
setAttribute()
method that achieves the same effect - we should specify
"use strict"
as a string at the top of our JS functions to ask the browser to be a bit less permissive about possible errors. For example, if a variable hasn't been declared (maybe because you mispelled it) the browser will complain instead of just creating a new global. - we can attach a function as an event handler for an event using
the
.addEventListener()
method. The function will be invoked by the browser whenever the event occurs - the function that we attach can either be named or anonymous, meaning a
function expression or function literal, like
function () { ... }
- the argument function is passed in without parens after it, because parens would invoke the function and only pass in the return value. Instead, we want to pass in the function itself
- the callback function is invoked with a JS object that contains a bunch of information about the event. This is called the event object
- the event object has a method called
preventDefault()
which, if invoked, will cause the browser not to do the "normal" thing, whatever that normal thing is. In Ottergram, we're clicking on a hyperlink, and the normal thing is to visit the URL specified in thehref
. We want to not do that, so the event handler executesevent.preventDefault
whereevent
is just the parameter that gets assigned the event object. - If we want to select more than one element (e.g. all the thumbnails), we can use
document.querySelectorAll()
Note that that returns a NodeList which is like an JavaScript array but doesn't support all the array methods. Best to iterate over it with afor
loop. Alternatively, convert it to a JS array using[].slice.call.(*nodelist*)
Raw JS versus jQuery¶
Your book decided to defer the introduction of jQuery. This is a fine decision; they're already throwing a lot at you in this chapter. Because we're spreading the content out over several meetings, I decided to introduce jQuery. So, let's talk about the correspondence between raw JS and JQ.
Raw JS | document.querySelector(sel_string) |
jQuery | $(sel_string) |
use | get a DOM element |
Raw JS | document.querySelectorAll(sel_string) |
jQuery | $(sel_string) |
use | get a list of DOM elements (jQuery always returns sets) |
Raw JS | elt.getAttribute(attrib_string) |
jQuery | jq_set.attr(attrib_string) |
use | get the value of an attribute |
Raw JS | elt.setAttribute(attrib_string,value_string) |
jQuery | jq_set.attr(attrib_string,value_string) |
use | set an attribute to a value |
Raw JS | elt.textContent = string |
jQuery | jq_set.text(string) |
use | set the text inside the element |
Raw JS | elt.addEventListener(event_type,callback_function) |
jQuery | jq_set.on(event_type, callback_function) |
use | attach an event handler to an element |
Raw JS | elt.addEventListener('click',callback_function) |
jQuery | jq_set.click(callback_function) |
use | attach a handler for click events to an element |
You see that the jQuery methods are similar to raw JS, just as intuitive, more consistent (they are always methods, while raw JS is a mixture of methods and attributes), and more concise.
Note that we didn't learn jQuery's .on()
method; instead, we learned
a special case for click handlers, namely click()
. I added on
above for completeness. on
has some advantages that we will discuss
later in the course, but click
is simple and easy.
Thumbnails¶
Note that in Ottergram, the same image file is used for both the thumbnail (small) and the detail (big) version. If there were many thumbnails, such that you expect most of them won't get clicked on, that would be a poor design, because your page would have downloaded many big images only to scale most of them down. Thus, your download would be increased for no benefit: a waste of time and bandwidth.
In Ottergram, there are only a few pictures, so the waste is negligible. In a different application, we might create special thumbnail versions. Note that their code doesn't assume that the URL of the detail image is the same as the URL of the thumbnail, so their code is ready for that improvement. That's good design.