reptile7's JavaScript blog
Saturday, April 24, 2010
Adventures in Event Cancelation
Blog Entry #177

We previously discussed HTML Goodies' "Checkboxes: Only Two" tutorial in Blog Entry #47 as part of a multipost discourse on the topic of data validation. Per its title, "Checkboxes: Only Two" presents a script that prevents a user from checking more than two checkboxes within a form containing three or more checkboxes. In the "Only two checkboxes, please" section of Blog Entry #47, we overhauled the "Checkboxes: Only Two" script, greatly reducing its volume of code by replacing its series of if statements with a for loop.

We conclude our tour of HTML Goodies' "JavaScript Form Scripts" series of tutorials in today's post by revisiting a 'value-added' aspect of the "Checkboxes: Only Two" script that goes beyond the script's immediate effect: for those events that are cancelable, the script clearly illustrates the syntax for canceling an event via a function. When the target for an event is an HTML element, the code for functionally canceling the event can be written in general form as follows:

<script type="text/javascript">
function checkEvent( ) {
[ if (condition) ] return false;
<element onEvent="return checkEvent( );">

In the above code, it is necessary
(a) for the checkEvent( ) function to return false AND
(b) for the element's onEvent handler to contain a return statement to act on the checkEvent( ) return
in order to cancel the event. Alternatively, an

elementObject.onevent = checkEvent;

JavaScript assignment statement can be used instead of the element's onEvent="return checkEvent( );" attribute; for example, Joe could have replaced the onClick="return KeepCount( );" input element attributes in the "Checkboxes: Only Two" script with the following script statement:

/* As top-level code, this statement should be placed in or referenced by a script element that appears after the joe form. */
for (i = 0; i < document.joe.elements.length; i++) { document.joe.elements[i].onclick = KeepCount; }

So, what can we do with our event cancelation code? What all can we cancel?

Cancelation scope and examples

With respect to classical JavaScript's event handlers, the following events can be canceled (in theory, at least):
• Key events: keydown, keypress, keyup
• Mouse events: click, dblclick, mousedown, mousemove, mouseout, mouseover, mouseup
• Form events: reset, submit
• According to Netscape, the dragdrop event should also be cancelable; however, the ondragdrop event handler is not supported by any of the OS X browsers on my computer. (Moreover, I find that Mozilla's updated, addEventListener( )-based method for canceling dragdrop events works with Camino but that's it.)

Given that the keyboard and the mouse constitute standard input for a GUI computer, it follows that we are able to preempt most of the ways a user can interact with a Web page.

Keypress cancelation demo

The demo in this subsection was adapted from this Netscape example. In the div below, the user is asked to enter a five-digit zip code into a text field. Click on the field and try to type a non-digit (letter or symbol) character into it.

Enter your five-digit zip code in the box below:

The demo uses the following script to prevent the keyboard entry of non-digit characters:
<script type="text/javascript">

function blockNondigits(e) {
	var thisKey = e ? e.which : (event ? event.keyCode : "");
	var keyChar = String.fromCharCode(thisKey);
	if (!/\d/.test(keyChar) && thisKey != 8) return false; }

document.zipForm.textentry.onkeypress = blockNondigits;

Upon attempting to type a character into the name="textentry" text field, the resulting keypress event calls the blockNondigits( ) function. blockNondigits( ) identifies the character of the pressed key via the event object's which or keyCode property in collaboration with fromCharCode( ), a cross-browser static method of the String object. The identified character, keyChar, is then compared to a /\d/ regular expression signifying a [0-9] digit: if they don't match, a return false; statement cancels the keypress event and thereby prevents keyChar from being entered into the textentry field.

The aforelinked Netscape example uses the cancelation of keydown events to prevent the user from typing an a or A character into a text field. I originally wrote the above demo with onkeydown as the blockNondigits( ) trigger and it worked fine with all of the OS X GUI browsers on my computer except for Opera, which 'hears' keydown events but evidently will not cancel them; switching onkeydown to onkeypress solved this problem.

Netscape's example uses the which property of the event object to obtain the ASCII/Unicode code position of the character for a depressed key. The which property is supported by all of the OS X GUI browsers on my computer except for MSIE, which uses an analogous keyCode property for this purpose. As detailed in Blog Entry #143, these different properties and the differing Netscape and Microsoft event models can be reconciled via a var thisKey = e ? e.which : (event ? event.keyCode : ""); conditional.

Regarding the example's var keyChar = String.fromCharCode(e.which); line, Netscape says, In the function, the which property of the event assigns the ASCII value of the key the user presses to the keyChar variable - not true: in fact, the e.which ASCII value is converted by the String.fromCharCode( ) method to the value's corresponding character, which is what is actually assigned to keyChar.

Lastly, to keep the backspace/delete key active, I added a thisKey != 8 subcondition to the declaration of the if statement that compares keyChar and /\d/.

The demo is not foolproof in the sense that non-digit characters can be mousically pasted into the textentry field via the browser's Edit menu.

Canceling link action and Boolean methods

The action of a link can also be conditionalized. In illustration, click on the link below:

Take me to the W3C's home page.

Here's the code, which was adapted from this Netscape example:

<a href="" target="_blank" onclick="return window.confirm('Click the OK button to open the W3C\'s home page in a new window.');">Take me to the W3C's home page.</a>

The confirm( ) method of the window object has a Boolean return: clicking the button on the confirm( ) box returns true; clicking the box's button returns false. No external code is required in this case, although you could use a separate function to enable/disable a link if you wanted to.

The test( ) method of the RegExp object, which we used in the keypress cancelation demo above, also has a Boolean return. If we were to add a <button>Submit your zip code</button> submit button to the keypress demo's form, then equipping the button element start-tag with an

onclick="return /^\d{5}$/.test(document.zipForm.textentry.value);"

attribute would prevent the button from submitting any textentry value that does not comprise five digits.

Don't take my stuff (please...)

Event cancelation can be used to provide for Web content a thin layer of anti-theft protection. For most JavaScript-enabled browsers, the following function expression will stop a user from selecting and copying the text on a Web page:

document.onmousedown = function ( ) { return false; }

Alternatively, you can accomplish the same thing by simply adding an onmousedown="return false;" attribute to the body element start-tag.

What about images, huh? Moving beyond classical JavaScript's event handlers, Microsoft implemented in MSIE 5 a now-cross-browser oncontextmenu event handler that can be used to suppress the right-click or control-click downloading of images via a context menu in an analogous manner:

document.oncontextmenu = function ( ) { return false; }
// Equivalently: <body oncontextmenu="return false;">

Moreover, the preceding onmousedown expression will prevent the download of an image via dragging it to the desktop.

In practice on my computer, the above onmousedown and oncontextmenu expressions work very nicely with Camino, Chrome, Firefox, and Safari (notwithstanding that Mozilla's Gecko DOM Reference associates oncontextmenu with the window object but not the document object Mozilla now associates oncontextmenu with both the window and document objects); the onmousedown code strangely doesn't work with MSIE (it should!) whereas the oncontextmenu code doesn't work with either MSIE or Opera. (Suffice it to say that just because MSIE for Windows supports a given feature does not mean that MSIE for the Mac also supports that feature.)

You can test your browser's support for these effects in the div below:

<title>Canceling Mousedown and Contextmenu Events</title>

Try to select me.

Try to right-click or control-click me: [Image of an 'Admit One' ticket]

Are there ways around these expressions? Of course - in particular, they can be thwarted by simply turning off the browser's "Enable JavaScript" preference. But if you're that terrified about users copying your content, maybe you shouldn't be uploading it to the Web in the first place.

In the name of completeness

There are two HTML-related default browser behaviors that are generated by mouseover events:
(1) Mousing over a link (having an underlying anchor or area element) puts the link's href value in the browser window's status bar.
(2) Mousing over an element with a title attribute pops up a tooltip.

Can these behaviors be suppressed with a return false; statement? "Yes with Opera, no with all other browsers" is the answer on my computer.

W3C event handler references

As HTML element attributes, most of classical JavaScript's event handlers were implemented by the W3C in HTML 4 - see Section 18.2.3 ("Intrinsic events") in the HTML 4.01 Specification. As you would expect, HTML5 will offer an expanded set of event handlers, including oncontextmenu. HTML 4 doesn't say anything about event cancelability; in contrast, the HTML5 Specification (if I'm reading it correctly) seems to stipulate that its various event types must all be cancelable by 'conforming user agents'.

FYI: HTML5 won't include ondragdrop, which is being splintered into a set of subhandlers (ondrag, ondragend, ...), nor onmove, which as an event handler for the window object seems to have been obsoleted by Netscape/Mozilla long ago.

User events are covered in more detail in the DOM Level 2 and Level 3 Events Specifications; these documents unambiguously state which events should and should not be cancelable. The new and cancelable events described by the DOM Level 3 Events Specification, which is currently at the Working Draft stage, are: compositionstart, compositionupdate, compositionend, mouseenter, mouseleave, mousewheel, textInput, and wheel - of these events, only mousewheel appears in the HTML5 Specification.

In the following entry, we'll move on to the next "Beyond HTML : JavaScript" tutorial, "Click... It's Copied!", which discusses a script that copies a text string to the operating system's clipboard. The "Click... It's Copied!" script doesn't work with any of the browsers on my computer, but that doesn't mean we can't chat about it a bit, does it?


Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)