The wisest prophets make sure of the event first.
—Horace Walpole
Do you think I can listen all day to such stuff?
—Lewis Carroll
The user should feel in control of the computer; not the other way around. This is achieved in applications that embody three qualities: responsiveness, permissiveness, and consistency.
—Inside Macintosh, Volume 1
Apple Computer, Inc., 1985
We are responsible for actions performed in response to circumstances for which we are not responsible.
—Allan Massie
We’ve seen that XHTML pages can be controlled via scripting, and we’ve already used a few events to trigger scripts, such as the onclick
and onsubmit
events. This chapter goes into more detail on JavaScript events, which allow scripts to respond to user interactions and modify the page accordingly. Events allow scripts to respond to a user who is moving the mouse, entering form data or pressing keys. Events and event handling help make web applications more responsive, dynamic and interactive.
In this chapter, we discuss how to set up functions to react when an event fires (occurs). We give examples of event handling for nine common events, including mouse events and form-processing events. A the end of the chapter, we provide a table of the events covered in this chapter and other useful events.
Functions that handle events are called event handlers. Assigning an event handler to an event on a DOM node is called registering an event handler. Previously, we have registered event handlers using the inline model, treating events as attributes of XHTML elements (e.g., <p onclick = "myfunction()">
). Another model, known as the traditional model, for registering event handlers is demonstrated alongside the inline model in Fig. 11.1.
In the earliest event-capable browsers, the inline model was the only way to handle events. Later, Netscape developed the traditional model and Internet Explorer adopted it. Since then, both Netscape and Microsoft have developed separate (incompatible) advanced event models with more functionality than either the inline or the traditional model. Netscape’s advanced model was adapted by the W3C to create a DOM Events Specification. Most browsers support the W3C model, but Internet Explorer 7 does not. This means that to create cross-browser websites, we are mostly limited to the traditional and inline event models. While the advanced models provide more convenience and functionality, most of the features can be implemented with the traditional model.
Line 35 assigns "handleEvent()"
to the onclick
attribute of the div
in lines 35–36. This is the inline model for event registration we’ve seen in previous examples. The div
in line 39 is assigned an event handler using the traditional model. When the body
element (lines 33–40) loads, the registerHandler
function is called.
Function registerHandler
(lines 25–29) uses JavaScript to register the function handleEvent
as the event handler for the onclick
event of the div
with the id "traditional"
. Line 27 gets the div
, and line 28 assigns the function handleEvent
to the div
’s onclick
property.
Notice that in line 28, we do not put handleEvent
in quotes or include parentheses at the end of the function name, as we do in the inline model in line 35. In the inline model, the value of the XHTML attribute is a JavaScript statement to execute when the event occurs. The value of the onclick
property of a DOM node is not an executable statement, but the name of a function to be called when the event occurs. Recall that JavaScript functions can be treated as data (i.e., passed into methods, assigned to variables, etc.).
Putting quotes around the function name when registering it using the inline model would assign a string to the onclick property of the node—a string cannot be called.
Putting parentheses after the function name when registering it using the inline model would call the function immediately and assign its return value to the onclick property.
Once the event handler is registered in line 28, the div
in line 39 has the same behavior as the div
in lines 35–36, because handleEvent
(lines 19–22) is set to handle the onclick
event for both div
s. When either div
is clicked, an alert will display "The event was successfully handled."
The traditional model allows us to register event handlers in JavaScript code. This has important implications for what we can do with JavaScript events. For example, traditional event-handler registration allows us to assign event handlers to many elements quickly and easily using repetition statements, instead of adding an inline event handler to each XHTML element. In the remaining examples in this chapter, we use both the inline and traditional registration models depending on which is more convenient.
The onload
event fires whenever an element finishes loading successfully (i.e., all its children are loaded). Frequently, this event is used in the body
element to initiate a script after the page loads in the client’s browser. Figure 11.2 uses the onload
event for this purpose. The script called by the onload
event updates a timer that indicates how many seconds have elapsed since the document was loaded.
Our use of the onload
event occurs in line 30. After the body
section loads, the browser triggers the onload
event. This calls function startTimer
(lines 15–19), which in turn uses method window.setInterval
to specify that function updateTime
(lines 22–26) should be called every 1000
milliseconds. The updateTime
function increments variable seconds
and updates the counter on the page.
Note that we could not have created this program without the onload event, because elements in the XHTML page cannot be accessed until the page has loaded. If a script in the head
attempts to get a DOM node for an XHTML element in the body
, getElementById
returns null
because the body
has not yet loaded. Other uses of the onload
event include opening a pop-up window once a page has loaded and triggering a script when an image or Java applet loads.
Trying to get an element in a page before the page has loaded is a common error. Avoid this by putting your script in a function using the onload event to call the function.
This section introduces the onmousemove
event, which fires repeatedly whenever the user moves the mouse over the web page. We also discuss the event
object and the keyword this
, which permit more advanced event-handling capabilities. Figure 11.3 uses onmousemove
and this
to create a simple drawing program that allows the user to draw inside a box in red or blue by holding down the Shift or Ctrl keys.
The XHTML body
has a table
with a tbody
containing one row that gives the user instructions on how to use the program. The body’s onload
attribute (line 61) calls function createCanvas
, which initializes the program by filling in the table.
The createCanvas
function (lines 23–41) fills in the table with a grid of cells. The CSS rule in lines 14–15 sets the width
and height
of every td
element to 4px. Line 11 dictates that the table is 400px
wide. Line 13 uses the border-collapse
CSS property to eliminate space between the table cells.
Line 25 defines variable side
, which determines the number of cells in each row and the number of rows created by the nested for
statements in lines 28–40. We set side
to 100 in order to fill the table with 10,000 4px
cells. Line 26 stores the tbody
element so that we can append rows to it as they are generated.
Although you can omit the tbody element in an XHTML table, without it you cannot append tr elements as children of a table using JavaScript. While Firefox treats appended rows as members of the table body, Internet Explorer will not render any table cells that are dynamically added to a table outside a thead, tbody or tfoot element.
The nested for
statements in lines 28–40 fill the table with a 100 × 100 grid of cells. The outer loop creates each table row, while the inner loop creates each cell. The inner loop uses the createElement
method to create a table cell, assigns function process-MouseMove
as the event handler for the cell’s onmousemove
event and appends the cell as a child of the row. The onmousemove event of an element fires whenever the user moves the mouse over that element.
At this point, the program is initialized and simply calls processMouseMove
whenever the mouse moves over any table cell. The function processMouseMove
(lines 44–57) colors the cell the mouse moves over, depending on the key that is pressed when the event occurs. Lines 44–48 get the event object, which stores information about the event that called the event-handling function.
Internet Explorer and Firefox do not implement the same event models, so we need to account for some differences in how the event
object can be handled and used. Firefox and other W3C-compliant browsers (e.g., Safari, Opera) pass the event
object as an argument to the event-handling function. Internet Explorer, on the other hand, stores the event object in the event
property of the window
object. To get the event object regardless of the browser, we use a two-step process. Function processMouseMove
takes the parameter e
in line 44 to get the event
object from Firefox. Then, if e
is undefined (i.e., if the client is Internet Explorer), we assign the object in window.event
to e
in line 48.
In addition to providing different ways to access the event
object, Firefox and Internet Explorer also implement different functionality in the event
object itself. However, there are several event
properties that both browsers implement with the same name, and some that both browsers implement with different names. In this book, we use properties that are implemented in both event models, or we write our code to use the correct property depending on the browser—all of our code runs properly in IE7 and Firefox 2 (and higher).
Once e
contains the event
object, we can use it to get information about the event. Lines 51–56 do the actual drawing. The event object’s ctrlKey property contains a boolean which reflects whether the Ctrl key was pressed during the event. If ctrlKey
is true, line 52 executes, changing the color of a table cell.
To determine which table cell to color, we introduce the this keyword. The meaning of this
depends on its context. In an event-handling function, this
refers to the DOM object on which the event occurred. Our function uses this
to refer to the table cell over which the mouse moved. The this
keyword allows us to use one event handler to apply a change to one of many DOM elements, depending on which one received the event.
Lines 51–52 change the background color of this
table cell to blue if the Ctrl key is pressed during the event. Similarly, lines 55–56 color the cell red if the Shift key is pressed. To determine this, we use the shiftKey property of the event
object. This simple function allows the user to draw inside the table on the page in red and blue.
This example demonstrated the ctrlKey
and shiftKey
properties of the event
object. Figure 11.4 lists some important cross-browser properties of the event
object.
This section introduced the event onmousemove
and the keyword this
. We also discussed more advanced event handling using the event
object to get information about the event. The next section continues our introduction of events with the onmouseover
and onmouseout
events.
Two more events fired by mouse movements are onmouseover
and onmouseout
. When the mouse cursor moves into an element, an onmouseover event occurs for that element. When the cursor leaves the element, an onmouseout event occurs. Figure 11.5 uses these events to achieve a rollover effect that updates text when the mouse cursor moves over it. We also introduce a technique for creating rollover images.
To create a rollover effect for the image in the heading, lines 20–23 create two new JavaScript Image
objects—image1
and image2
. Image image2
displays when the mouse hovers over the image. Image image1
displays when the mouse is outside the image. The script sets the src
properties of each Image
in lines 21 and 23, respectively. Creating Image
objects preloads the images (i.e., loads the images in advance), so the browser does not need to download the rollover image the first time the script displays the image. If the image is large or the connection is slow, downloading would cause a noticeable delay in the image update.
Preloading images used in rollover effects prevents a delay the first time an image is displayed.
Functions mouseOver
and mouseOut
are set to process the onmouseover
and onmouseout
events, respectively, in lines 74–75. Both functions begin (lines 25–28 and 45–48) by getting the event
object and using function getTarget
to find the element that received the action. Because of browser event model differences, we need getTarget
(defined in lines 66–72) to return the DOM node targeted by the action. In Internet Explorer, this node is stored in the event
object’s srcElement property. In Firefox, it is stored in the event
object’s target property. Lines 68–71 return the node using the correct property to hide the browser differences from the rest of our program. We must use function getTarget
instead of this
because we do not define an event handler for each specific element in the document
. In this case, using this
would return the entire document. In both mouseOver
and mouseOut
, we assign the return value of getTarget
to variable target
(lines 30 and 50).
Lines 33–37 in the mouseOver
function handle the onmouseover
event for the heading image by setting its src
attribute (target.src
) to the src
property of the appropriate Image
object (image2.src
). The same task occurs with image1
in the mouseOut
function (lines 53–57).
The script handles the onmouseover
event for the table cells in lines 41–42. This code tests whether an id
is specified, which is true only for our hex code table cells and the heading image in this example. If the element receiving the action has an id
, the code changes the color of the element to match the color name stored in the id
. As you can see in the code for the table
(lines 86–111), each td
element containing a color code has an id
attribute set to one of the 16 basic XHTML colors. Lines 61–62 handle the onmouseout
event by changing the text in the table cell the mouse cursor just left to match the color that it represents.
The onfocus
and onblur
events are particularly useful when dealing with form elements that allow user input (Fig. 11.6). The onfocus
event fires when an element gains focus (i.e., when the user clicks a form field or uses the Tab key to move between form elements), and onblur
fires when an element loses focus, which occurs when another control gains the focus. In lines 31–32, the script changes the text inside the div
below the form (line 58) based on the messageNum
passed to function helpText
(lines 29–33). Each of the elements of the form, such as the name
input in lines 40–41, passes a different value to the helpText
function when it gains focus (and its onfocus
event fires). These values are used as indices for helpArray
, which is declared and initialized in lines 17–27 and stores help messages. When elements lose focus, they all pass the value 6
to helpText
to clear the tip div
(note that the empty string ""
is stored in the last element of the array).
Two more useful events for processing forms are onsubmit
and onreset
. These events fire when a form is submitted or reset, respectively (Fig. 11.7). Function registerEvents
(lines 35–46) registers the event handlers for the form after the body has loaded.
Lines 37–40 and 42–45 introduce several new concepts. Line 37 gets the form
element ("myForm"
, lines 51–70), then lines 37–40 assign an anonymous function to its onsubmit
property. An anonymous function is defined with no name—it is created in nearly the same way as any other function, but with no identifier after the keyword function
. This notation is useful when creating a function for the sole purpose of assigning it to an event handler. We never call the function ourselves, so we don’t need to give it a name, and it’s more concise to create the function and register it as an event handler at the same time.
The anonymous function (lines 37–40) assigned to the onsubmit
property of myForm
executes in response to the user submitting the form (i.e., clicking the Submit button or pressing the Enter key). Line 39 introduces the confirm method of the window object. As with alert
, we do not need to prefix the call with the object name window
and the dot (.
) operator. The confirm dialog asks the users a question, presenting them with an OK button and a Cancel button. If the user clicks OK, confirm
returns true
; otherwise, confirm
returns false
.
Our event handlers for the form’s onsubmit
and onreset
events simply return the value of the confirm
dialog, which asks the users if they are sure they want to submit or reset (lines 39 and 44, respectively). By returning either true
or false
, the event handlers dictate whether the default action for the event—in this case submitting or resetting the form—is taken. (Recall that we also returned false
from some event-handling functions to prevent forms from submitting in Chapter 10.) Other default actions, such as following a hyperlink, can be prevented by returning false
from an onclick
event handler on the link. If an event handler returns true
or does not return a value, the default action is taken once the event handler finishes executing.
Event bubbling is the process by which events fired in child elements “bubble” up to their parent elements. When an event is fired on an element, it is first delivered to the element’s event handler (if any), then to the parent element’s event handler (if any). This might result in event handling that was not intended. If you intend to handle an event in a child element alone, you should cancel the bubbling of the event in the child element’s event-handling code by using the cancelBubble property of the event
object, as shown in Fig. 11.8.
Clicking the first p
element (line 45) triggers a call to bubble
. Then, because line 37 registers the document
’s onclick
event, documentClick
is also called. This occurs because the onclick
event bubbles up to the document
. This is probably not the desired result. Clicking the second p
element (line 46) calls noBubble
, which disables the event bubbling for this event by setting the cancelBubble
property of the event
object to true
. [Note: The default value of cancelBubble
is false
, so the statement in line 23 is unnecessary.]
Forgetting to cancel event bubbling when necessary may cause unexpected results in your scripts.
The events we covered in this chapter are among the most commonly used. A list of some events supported by both Firefox and Internet Explorer is given with descriptions in Fig. 11.9.
http://www.quirksmode.org/js/introevents.html
An introduction and reference site for JavaScript events. Includes comprehensive information on history of events, the different event models, and making events work across multiple browsers.
wsabstract.com/dhtmltutors/domevent1.shtml
This JavaScript Kit tutorial introduces event handling and discusses the W3C DOM advanced event model.
http://www.w3schools.com/jsref/jsref_events.asp
The W3 School’s JavaScript Event Reference site has a comprehensive list of JavaScript events, a description of their usage and their browser compatibilities.
http://www.brainjar.com/dhtml/events/
BrainJar.com’s DOM Event Model site provdes a comprehensive introduction to the DOM event model, and has example code to demonstrate several different ways of assigning and using events.