9.4 Preventing an Event from Performing Its Default Behavior
NN 6, IE 5
9.4.1 Problem
You
want
to prevent an event from triggering its
default behavior.
9.4.2 Solution
Although it is a fruitless endeavor to use scripts to block users
from, say, right-clicking on an image to save a copy of the image on
the local hard disk, this recipe shows how to use event blocking to
discourage casual users who may be dissuaded by an alert message.
The primary event to block in this case is the
oncontextmenu event, which is implemented in IE
5 or later for Windows and Netscape 6 or later. Assign a function to
the event at the document level. The following function blocks the
event for all img elements:
function blockEvents(evt) {
evt = (evt) ? evt : event;
var blockit = false;
var elem = (evt.target) ? evt.target : ((evt.srcElement) ?
evt.srcElement : null);
if (elem && elem.tagName && elem.tagName.toLowerCase( ) = = "img") {
if (evt.cancelBubble) {
evt.cancelBubble = true;
}
alert("Sorry, feature not available.");
return false;
}
}
document.oncontextmenu = blockEvents;
9.4.3 Discussion
Inhibiting an event's default action can give your
dynamic pages powers they couldn't have on their
own. For example, if you want to limit text field entry to numbers
only, examine the event object details and block nonnumeric
characters from reaching the field. Similarly, if you are performing
client-side form validation (see Chapter 8) when
the user submits the form, you want to block the
submit event from carrying out its default
behavior if the validation fails.
Before browsers had the sophisticated event models of today, events
could still be blocked, although on a more limited basis. The basic
technique was to make sure that the event handler's
last in-line statement evaluated to the expression:
return false;
For example, in Recipe 5.10, you see how an a
element can link to one document through its traditional
href reference when the browser
isn't scriptable, but navigate to another page when
scripting is available:
<a href="std/newProds.html" title="To New Products"
onclick="return linkTo('ieWin/newProds.html', 'w3/newProds.html')">New
Products</a>
In this case, the linkTo( ) function returns a
value of false so that the
onclick event handler evaluates to
return false. The
a element never acts on the
href link when scripting is enabled because as far
as the element is concerned, the click never happened. You could also
format the onclick event handler as two separate
statements in a series:
onclick="linkTo('ieWin/newProds.html', 'w3/newProds.html'); return false"
The first format is ideal when the function invoked by the event
handler processes the event with the goal of discovering whether the
default action of the event should be permitted to pass to the
element. See Recipe 8.3 for this technique to be used with form
validation and Recipe 8.11 to allow only desired characters in a text
field.
As the event models increased in sophistication, the old ways still
worked (and still do), but events were also being bound to elements
in ways that did not permit the direct inclusion of a
return false statement in the
binding assignment. Instead, when events are bound to an element by
way of property assignment, the last executing statement of the
handler function dictates whether the default action of the event
passes to the element. Therefore, if the last statement of the
function is return true, the
default action is processed normally; a final
return false prevents the
default action.
This technique was improved syntactically in the IE 4 event model
with the addition of the event
object's returnValue property.
You can set this property to true or
false to direct the target element to process or
ignore the default behavior, respectively. A side benefit of this
extra property is that you can return something other than a Boolean
value from the function, yet the default behavior is controlled
independently.
Comparable capabilities are built into the W3C DOM event model by way
of a method of the event object passed to the handler function.
Invoke the event object's preventDefault(
) method (no parameters) to tell the target element to
ignore the event. Calling this method in Netscape 6 or later is the
same as setting the event.returnValue property to
false in IE 4 or later. Of course, the cross-browser choice is the
old-fashioned return false
approach.
Consider event propagation in your event processing. The IE 4 and W3C
DOM event models allow events to propagate up the document node tree.
For example, a click event on a
form's button fires on the button, on the
form element, on the body
element, and on the root document node. You might
wish to take advantage of this feature if you have a series of
similar objects and want a single event handler to process a
particular event on all of those elements—let the events from
the individual elements bubble up to a container node, whose event
handler for that event type triggers a function.
Event propagation in the direction of the document
node is called event
bubbling (i.e., the event
"bubbles" upward through the node
tree). If you have event handler definitions assigned for nodes high
up on the tree, you may need to block events of the same type from
bubbling beyond the elements in which they do their work. Otherwise,
the events will trigger those higher-up event handler functions and
mess up your processing goals.
To help control event bubbling, IE 4 implemented the
cancelBubble property of the
event object. Netscape 6 and later
implement this property, too, as a cross-browser convenience
(it's not part of the W3C DOM Level 2 event model).
If you set this property to true, the event does
not bubble beyond the node that is processing the event at the time.
The W3C DOM version of this feature is the stopPropagation(
) method of the event object. So far, this
method is implemented only in Netscape 6 and later.
Here's an object model-specific revised version of
the blockEvents( ) function for IE:
function blockEvents( ) {
var blockit = false;
var elem = event.srcElement;
if (elem && elem.tagName && elem.tagName.toLowerCase( ) = = "img") {
event.cancelBubble = true;
event.returnValue = false;
alert("Sorry, feature not available.");
}
}
In pure W3C DOM syntax, the function becomes:
function blockEvents(evt) {
var blockit = false;
var elem = (evt.target) ;
if (elem && elem.tagName && elem.tagName.toLowerCase( ) = = "img") {
evt.preventDefault( );
evt.stopPropagation( );
alert("Sorry, feature not available.");
}
}
Despite all the best efforts of web content
developers, there is no defense against the determined page visitor
who wants to view your page source code (including linked JavaScript
libraries and CSS style sheets) or capture a copy of an image file
showing in the page. That's not the only reason to
block events, but it is perhaps the most common request among content
providers. Any scripted technique is immediately defeatable by the
user turning off scripting in the browser. Even if you try to
disguise things by opening the page in a menu bar-less window, the
URL to that page is accessible in the loading page so that the URL
can be opened manually in a regular browser window. As weak as this
level of "right-click" protection
is, plenty of content developers observe the technique on other pages
and are convinced it offers genuine blockage. They frequently inquire
in online forums for guidance in implementing what
they've seen. This provides anecdotal evidence that
even many experienced developers can be dissuaded by this simple
event trick.
9.4.4 See Also
Other recipes that control event propagation or default actions:
Recipe 5.10 for hyperlinks; Recipe 8.3 for form submissions; Recipe 8.11 for text boxes.
|