DekGenius.com
[ Team LiB ] Previous Section Next Section

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.

    [ Team LiB ] Previous Section Next Section