52
17.1.4 Touchscreen and Mobile Events
The widespread adoption of powerful mobile devices, particularly those with
touchscreens, has required the creation of new categories of events. In many cases,
touchscreen events are mapped to traditional event types such as click and scroll. But
not every interaction with a touchscreen UI emulates a mouse, and not all touches can
be treated as mouse events. This section briefly explains the gesture and touch events
generated by Safari when running on Apple’s iPhone and iPad devices and also covers
the orientationchange event generated when the user rotates the device. At the time of
this writing, there are no standards for these events, but the W3C has begun work on
a “Touch Events Specification” that uses Apple’s touch event as a starting point. These
events are not documented in the reference section of this book, but you can find more
information at the Apple Developer Center.
Safari generates gesture events for two-finger scaling and rotation gestures. The
gesturestart event is fired when the gesture begins and gestureend is fired when it ends.
Between these two events are a sequence of gesturechange events that track the progress
of the gesture. The event object sent with these events has numeric
scale
and
rotation
properties. The
scale
property is the ratio of the current distance between the
two fingers to the initial distance between the fingers. A “pinch close” gesture has a
scale
less than 1.0, and a “pinch open” gesture has a
scale
greater than 1.0. The
rotation
property is the angle of finger rotation since the start of the event. It is reported
in degrees, with positive values indicating clockwise rotation.
Gesture events are high-level events that notify you of a gesture that has already been
interpreted. If you want to implement your own custom gestures, you can listen for
low-level touch events. When a finger touches the screen a touchstart event is triggered.
When the finger moves, a touchmove event is triggered. And when the finger is lifted
from the screen, a touchend event is triggered. Unlike mouse events, touch events do
not directly report the coordinates of the touch. Instead, the object sent with a touch
event has a
changedTouches
property. This property is an array-like object whose ele-
ments each describe the position of a touch.
The orientationchanged event is triggered on the Window object by devices that allow
the user to rotate the screen from portrait to landscape mode. The object passed with
an orientationchanged event is not useful itself. In mobile Safari, however, the
orientation
property of the Window object gives the current orientation as one of the
numbers 0, 90, 180, or -90.
17.2 Registering Event Handlers
There are two basic ways to register event handlers. The first, from the early days of
the Web, is to set a property on the object or document element that is the event target.
The second, newer and more general, technique is to pass the handler to a method of
the object or element. To complicate matters, there are two versions of each technique.
456 | Chapter 17: Handling Events
56
You can set an event handler property in JavaScript code, or for document elements,
you can set the corresponding attribute directly in HTML. For handler registration by
method invocation, there is a standard method, named
addEventListener()
, that is
supported by all browsers except IE8 and before, and a different method, named
attachEvent()
, for all versions of IE before IE9.
17.2.1 Setting Event Handler Properties
The simplest way to register an event handler is by setting a property of the event target
to the desired event handler function. By convention, event handler properties have
names that consist of the word “on” followed by the event name:
onclick
,
onchange
,
onload
,
onmouseover
, and so on. Note that these property names are case sensitive and
are written in all lowercase, even when the event type (such as “readystatechange”
consists of multiple words. Here are two example event handler registrations:
// Set the onload property of the Window object to a function.
// The function is the event handler: it is invoked when the document loads.
window.onload = function() {
// Look up a <form> element
var elt = document.getElementById("shipping_address");
// Register an event handler function that will be invoked right
// before the form is submitted.
elt.onsubmit = function() { return validate(this); }
}
This event handler registration technique works in all browsers for all commonly used
event types. In general, all widely implemented web APIs that define events allow han-
dlers to be registered by setting event handler properties.
The shortcoming of event handler properties is that they are designed around the as-
sumption that event targets will have at most one handler for each type of event. If you
are writing library code for use in arbitrary documents, it is better to register event
handlers using a technique (such as
addEventListener()
) that will not modify or over-
write any previously registered handlers.
17.2.2 Setting Event Handler Attributes
The event handler properties of a document element can also be set as attributes on
the corresponding HTML tag. If you do this, the attribute value should be a string of
JavaScript code. That code should be the body of the event handler function, not a
complete function declaration. That is, your HTML event handler code should not be
surrounded by curly braces and prefixed with the
function
keyword. For example:
<button onclick="alert('Thank you');">Click Here</button>
If an HTML event handler attribute contains multiple JavaScript statements, you must
remember to separate those statements with semicolons or to break the attribute value
across multiple lines.
17.2 Registering Event Handlers | 457
Client-Side
JavaScript
VB.NET PDF: Basic SDK Concept of XDoc.PDF XDoc.PDF for .NET allows VB.NET developers to edit hyperlink of PDF document, including editing PDF url links and quick navigation link in bookmark/outline.
clickable links in pdf files; add link to pdf file
63
Some event types are directed at the browser as a whole, rather than at any particular
document element. In JavaScript, handlers for these events are registered on the Win-
dow object. In HTML, we place them on the
<body>
tag, but the browser registers them
on the Window. The following is the complete list of such event handlers as defined
by the draft HTML5 specification:
onafterprint onfocus ononline onresize
onbeforeprint onhashchange onpagehide onstorage
onbeforeunload onload onpageshow onundo
onblur onmessage onpopstate onunload
onerror onoffline onredo
When you specify a string of JavaScript code as the value of an HTML event handler
attribute, the browser converts your string into a function that looks something
like this:
function(event) {
with(document) {
with(this.form || {}) {
with(this) {
/* your code here */
}
}
}
}
If the browser supports ES5, the function is defined in non-strict mode (see §5.7.3).
We’ll see more about the
event
argument and the
with
statements when we consider
event handler invocation in §17.3.
A common style in client-side programming involves keeping HTML content separate
from JavaScript behavior. Programmers who follow this discipline shun (or at least
avoid) HTML event handler attributes, since they directly mix JavaScript and HTML.
17.2.3 addEventListener()
In the standard event model supported by all browsers other than IE8 and earlier, any
object that can be an event target—this includes the Window and Document objects
and all document Elements—defines a method named
addEventListener()
that you
can use to register an event handler for that target.
addEventListener()
takes three
arguments. The first is the event type for which the handler is being registered. The
event type (or name) is a string and it should not include the “on” prefix that is used
when setting event handler properties. The second argument to
addEventListener()
is
the function that should be invoked when the specified type of event occurs. The final
argument to
addEventListener()
is a boolean value. Normally, you’ll pass
false
for this
argument. If you pass
true
instead, your function is registered as a capturing event
handler and is invoked at a different phase of event dispatch. We’ll cover event cap-
turing in §17.3.6. You ought to be able to omit the third argument instead of passing
false
, and the specification may eventually change to allow this, but at the time of this
writing, omitting that argument is an error in some current browsers.
458 | Chapter 17: Handling Events
78
The code below registers two handlers for the click event on a
<button>
element. Note
the differences between the two techniques used:
<button id="mybutton">Click me</button>
<script>
var b = document.getElementById("mybutton");
b.onclick = function() { alert("Thanks for clicking me!"); };
b.addEventListener("click", function() { alert("Thanks again!"); }, false);
</script>
Calling
addEventListener()
with “click” as its first argument does not affect the value
of the
onclick
property. In the code above, a button click will generate two
alert()
dialog boxes. More importantly, you can call
addEventListener()
multiple times to
register more than one handler function for the same event type on the same object.
When an event occurs on an object, all of the handlers registered for that type of event
are invoked, in the order in which they were registered. Invoking
addEventListener()
more than once on the same object with the same arguments has no effect—the handler
function remains registered only once, and the repeated invocation does not alter the
order in which handlers are invoked.
addEventListener()
is paired with a
removeEventListener()
method that expects the
same three arguments but removes an event handler function from an object rather
than adding it. It is often useful to temporarily register an event handler and then remove
it soon afterward. For example, when you get a mousedown event, you might register
temporary capturing event handlers for mousemove and mouseup events so that you
can see if the user drags the mouse. You’d then deregister these handlers when the
mouseup event arrives. In such a situation, your event handler removal code might look
like this:
document.removeEventListener("mousemove", handleMouseMove, true);
document.removeEventListener("mouseup", handleMouseUp, true);
17.2.4 attachEvent()
Internet Explorer, prior to IE9, does not support
addEventListener()
and
removeEventListener()
. In IE5 and later, it defines similar methods
attachEvent()
and
detachEvent()
.
The
attachEvent()
and
detachEvent()
methods work like
addEventListener()
and
removeEventListener()
, with the following exceptions:
• Since the IE event model does not support event capturing,
attachEvent()
and
detachEvent()
expect only two arguments: the event type and the handler function.
• The first argument to the IE methods is an event handler property name, with the
“on” prefix, rather than the unprefixed event type. For example, pass “onclick” to
attachEvent()
where you would pass “click” to
addEventListener()
.
•
attachEvent()
allows the same event handler function to be registered more than
once. When an event of the specified type occurs, the registered function will be
invoked as many times as it was registered.
17.2 Registering Event Handlers | 459
Client-Side
JavaScript
65
It is common to see event handler registration code that uses
addEventListener()
in
browsers that support it and otherwise uses
attachEvent()
:
var b = document.getElementById("mybutton");
var handler = function() { alert("Thanks!"); };
if (b.addEventListener)
b.addEventListener("click", handler, false);
else if (b.attachEvent)
b.attachEvent("onclick", handler);
17.3 Event Handler Invocation
Once you’ve registered an event handler, the web browser will invoke it automatically
when an event of the specified type occurs on the specified object. This section describes
event handler invocation in detail, explaining event handler arguments, the invocation
context (the
this
value), the invocation scope, and the meaning of the return value of
an event handler. Unfortunately, some of these details are different for IE8 and before
than for other browsers.
In addition to describing how individual handlers are invoked, this section also explains
how events propagate: how a single event can trigger the invocation of multiple handlers
on the original event target and also on containing elements of the document.
17.3.1 Event Handler Argument
Event handlers are normally (there is one exception, described below) invoked with an
event object as their single argument. The properties of the event object provide details
about the event. The
type
property, for example, specifies the type of the event that
occurred. §17.1 mentioned a number of other event object properties for various event
types.
In IE8 and before, event handlers registered by setting a property are not passed an
event object when they are invoked. Instead, the event object is available through the
global variable
window.event
. For portability, you can write event handlers like this, so
that they use the
window.event
if no argument is supplied:
function handler(event) {
event = event || window.event;
// Handler code goes here
}
Event handlers registered with
attachEvent()
are passed an event object, but they can
also use
window.event
.
Recall from §17.2.2 that when you register an event handler by setting an HTML at-
tribute, the browser converts your string of JavaScript code into a function. Browsers
other than IE construct a function with a single argument named
event
. IE constructs
a function that expects no argument. If you use the identifier
event
in such a function,
you are referring to
window.event
. In either case, HTML event handlers can refer to the
event object as
event
.
460 | Chapter 17: Handling Events
64
17.3.2 Event Handler Context
When you register an event handler by setting a property, it looks as if you are defining
a new method on the document element:
e.onclick = function() { /* handler code */ };
It isn’t surprising, therefore, that event handlers are invoked (with one IE-related ex-
ception, described below) as methods of the object on which they are defined. That is,
within the body of an event handler, the
this
keyword refers to the event target.
Handlers are invoked with the target as their
this
value even when registered using
addEventListener()
. Unfortunately, however, this is not true for
attachEvent()
: han-
dlers registered with
attachEvent()
are invoked as functions, and their
this
value is the
global (Window) object. You can work around this with code like this:
/*
* Register the specified handler function to handle events of the specified
* type on the specified target. Ensure that the handler will always be
* invoked as a method of the target.
*/
function addEvent(target, type, handler) {
if (target.addEventListener)
target.addEventListener(type, handler, false);
else
target.attachEvent("on" + type,
function(event) {
// Invoke the handler as a method of target,
// passing on the event object
return handler.call(target, event);
});
}
Note that event handlers registered using this method cannot be removed, since the
wrapper function passed to
attachEvent()
is not retained anywhere to be passed to
detachEvent()
.
17.3.3 Event Handler Scope
Like all JavaScript functions, event handlers are lexically scoped. They are executed in
the scope in which they are defined, not the scope from which they are invoked, and
they can access any local variables from that scope. (This is demonstrated in the
addEvent()
function above, for example.)
Event handlers registered as HTML attributes are a special case, however. They are
converted into top-level functions that have access to global variables but not to any
local variables. But, for historical reasons, they run with a modified scope chain. Event
handlers defined by HTML attributes can use the properties of the target object, the
containing
<form>
object (if there is one), and the Document object as if they are local
variables. §17.2.2 shows how an event handler function is created from an HTML event
handler attribute, and the code there approximates this modified scope chain using
with
statements.
17.3 Event Handler Invocation | 461
Client-Side
JavaScript
80
HTML attributes are not natural places to include long strings of code, and this modi-
fied scope chain allows helpful shortcuts. You can use
tagName
instead of
this.tag
Name
. You can use
getElementById
instead of
document.getElementById
. And, for docu-
ment elements that are inside a
<form>
, you can refer to any other form element by ID,
using
zipcode
, for example, instead of
this.form.zipcode
.
On the other hand, the modified scope chain of HTML event handlers is a source of
pitfalls, since the properties of each of the objects in the chain shadow any properties
of the same name in the global object. The Document object defines a (rarely used)
open()
method, for example, so an HTML event handler that wants to invoke the
open()
method of the Window object must explicitly write
window.open
instead
of
open
. There is a similar (but more pernicious) problem with forms, because the names
and IDs of form elements define properties on the containing form element (see
§15.9.1). So if a form contains an element with the ID “location”, for example, all
HTML event handlers within that form must use
window.location
instead of
location
if they want to refer to the window’s Location object.
17.3.4 Handler Return Value
The return value of an event handler registered by setting an object property or an
HTML attribute is sometimes significant. In general, a return value of
false
tells the
browser that it should not perform the default action associated with the event. The
onclick
handler of a Submit button in a form, for example, can return
false
to prevent
the browser from submitting the form. (This is useful if the user’s input fails client-side
validation.) Similarly, an
onkeypress
handler on an input field can filter keyboard input
by returning
false
if the user types an inappropriate character. (Example 17-6 filters
keyboard input in this way.)
The return value of the
onbeforeunload
handler of the Window object is also significant.
This event is triggered when the browser is about to navigate to a new page. If this event
handler returns a string, it will be displayed in a modal dialog box that asks the user to
confirm that she wants to leave the page.
It is important to understand that event handler return values are significant only for
handlers registered as properties. We’ll see below that event handlers registered with
addEventListener()
or
attachEvent()
must instead call the
preventDefault()
method
or set the
returnValue
property of the event object.
17.3.5 Invocation Order
A document element or other object may have more than one event handler registered
for a particular type of event. When an appropriate event occurs, the browser must
invoke all of the handlers, following these rules of invocation order:
• Handlers registered by setting an object property or HTML attribute, if any, are
always invoked first.
462 | Chapter 17: Handling Events
Documents you may be interested
Documents you may be interested