Section G. The example scripts


G. The example scripts

Let's take a look at the specific event solutions I devised for the eight example scripts. These explanations will serve as examples of selecting events, registration models, targeting, and practical uses of event bubbling.

Textarea Maxlength

Obviously, Textarea Maxlength needs a key event. Whenever the user adds another character, the script checks if the maximum length has been exceeded. I chose the keyup event for several reasons:

  • It fires only once when the user releases the key, and therefore: my script is called only once per keystroke. In general, I like to call functions as infrequently as possible.

  • Since we don't need to cancel the default action (i.e., prevent the pressed key's character from being added to the textarea), we don't need keypress.

  • We don't need to know which key the user pressed. (If we did, we wouldn't be able to use keypress.)

Nonetheless, the keyup event is not enough. Users might also paste text into the textarea without ever pressing a key. In that case, keyup wouldn't fire. Therefore I also use the change event: if the user leaves the textarea and its content has changed, the script runs.

So my event handlers become:

[Textarea Maxlength, line 16]

textareas[i].onkeyup = textareas[i].onchange = checkMaxLength; 


If either the keyup or the change event occurs, checkMaxLength() is executed. Neither are mouse events, so there shouldn't be any problem accessibility-wise.

Textarea Maxlength uses the this keyword for targeting, as we already saw in 7F. There's an extra complication, though: checkMaxLength() should change the <span> element next to the textarea. This element is neither the event target nor the element the event handler is defined on, so neither this nor the target will point to it.

Instead, the script creates a reference to the <span> on the textarea:

[Textarea Maxlength, line 15]

textareas[i].relatedElement = [the span]; 


Later on, checkMaxLength() uses this reference. We discussed this trick in 4C.

Usable Forms

Picking the right events for Usable Forms is tricky. The script needs to know if the user has changed any form field with a rel attribute. A change event would seem the obvious choice, but I decided not to use it for two reasons.

The page I originally wrote Usable Forms for contained 142 form fields, and I didn't want to set a separate event on every one of them. Instead, I wanted to use event bubbling. Unfortunately, change is an interface event and cannot be registered on the document in most browsers. Therefore, I decided to use the click event. Registering an onclick event handler on the document is always possible, and therefore the event bubbles up nicely.

In addition, in Explorer the change event on radio buttons and checkboxes fires only when the user blurs them, i.e., when he clicks or tabs somewhere else. This is a serious problem in Usable Forms' interface. It should be totally clear to the user that checking or unchecking certain form fields causes the form to change. We can't postpone the form change until later, since that could confuse some users.

I use addEventSimple(), because Usable Forms is a module that can be added to any site, and the site may already have a document.onclick event handler:

[Usable Forms, line 53]

addEventSimple(document,'click',showHideFields); 


There are no accessibility issues, since a click event also fires when the user activates form fields by hitting the space bar.

Unfortunately, the general click event doesn't cover changes to select boxes, since click events on options or selects are not supported. For select boxes I needed the change event, and its lack of bubbling capabilities forced me to add individual change events to all selects:

[Usable Forms, lines 22-23]

var selects = document.getElementsByTagName('select'); for (var i=0;i<selects.length;i++)     addEventSimple(selects[i],'change',showHideFields) 


We discussed Usable Forms' need for the event target in 7F.

Form Validation

Obviously, Form Validation needs the submit event. When the user submits the form, the script checks the content of all form fields:

[Form Validation, line 67]

forms[i].onsubmit = validate; 


The validate() function returns either true or false, so that the event knows whether to allow the form submission or not.

I don't need addEventSimple(), even though Form Validation is a module. If there is another form-validation routine that also uses the submit event, only one of the two routines should be executed. Therefore, I overwrite any existing onsubmit event handlers without a qualm.

The main validation function uses the this keyword to find the correct form (after all, there can be multiple forms on a single page), but the real action takes place on the form's elements:

[Form Validation, lines 73-74]

function validate() {     var els = this.elements;     // validate all elements 


I use a second event in Form Validation. When the script finds an error, it gives the offending form field special styles. When the user changes the value of the form field, I politely assume that she has corrected the error, and the special styles should be removed.

In order to remove the styles, I use the change event once more. If the user changes the value, assume there is no more error:

[Form Validation, lines 108-110 and 122-129 condensed]

function writeError(obj,message) {    obj.className += ' errorMessage';    obj.onchange = removeError;    // administration } function removeError() {    this.className = this.className.replace(/errorMessage/,'');    // administration    this.onchange = null; } 


I set this event only on those form fields that actually contain an error. When the user changes the form-field value, removeError() is called, and it removes the class and the change event. After that's done, the event is not useful any more, and since I prefer to clean up this sort of loose ends, I therefore set it to null.

Dropdown Menu

In Dropdown Menu I use the same event-bubbling trick as in Usable Forms. I set a mouseover and a mouseout event for every <ul >, which fire when the user mouses over or out of any link in the dropdown menu:

[Dropdown Menu, lines 6-11]

function initNavigation() {    var x = document.getElementsByTagName('ul');    for (var i=0;i<x.length;i++) {            if (x[i].className != 'menutree') continue;            x[i].onmouseover = navMouseOver;            x[i].onmouseout = navMouseOut; 


But now the menu is inaccessible without a mouse. Therefore, I also added a focus event. Unfortunately, the focus event does not bubble, so I'm forced to add it to all links inside <li>s that have a <ul> childin other words, all links that should trigger a new dropdown menu.

[Dropdown Menu, lines 12-16]

var listItems = lists[i].getElementsByTagName('li'); for (var j=0;j<listItems.length;j++) {     var test = listItems[j].getElementsByTagName('ul')[0];     if (test) { // li contains a nested ul            listItems[j].firstChild.onfocus = navMouseOver; 


Note that the event is set not on the <li> itself but on the <a> that is its first child, because <li>s are unable to receive the keyboard focus.

I do not use the blur event to simulate the mouseout, because it adds little to the overall user experience. If the user focuses on another link, all dropdown menus that are not children of this link are hidden anyway.

By adding the focus event, I've made Dropdown Menus as accessible as possible. Nonetheless, when you tab through the menu you'll find that it doesn't behave quite the same as when you use the mouse. Besides, in order to reach the last link in the menu, you have to tab through all other links first, while the mouse events allow you to take a shortcut.

This user experience can be vastly improved by adding arrow-key navigation to the script. I didn't do so, but the idea would be to register a general onkeydown event handler to all <ul>s, and then to read out the arrow key the user has pressed, and focus on the next (or previous) link.

Since Dropdown Menu uses event bubbling, all functions look for the event target and go on from there. In 7F we already discussed the extreme care you have to take when using event targets of trigger-happy events like mouseover.

Edit Style Sheet

At first glance, Edit Style Sheet seems very similar to Usable Forms. The script should run whenever the user changes any value of any form field, and I could again have opted for a general click event.

There's one important difference between the two scripts, though: Edit Style Sheet also works with text fields in which the user can enter data, while Usable Forms only works with radio buttons, checkboxes, and selects. A click event on a text field doesn't give much information, since at the time it occurs the field is most likely empty.

Therefore, in Edit Style Sheet I cannot replace the change event with the click event, and since the change event doesn't bubble up, I have to set it on every single field in the correct form:

[Edit Style Sheet, lines 33-35]

var els = document.forms[1].elements; for (var i=0;i<els.length;i++) {     els[i].onchange = assignStyles; 


The script uses the this keyword to find the form field the user has changed:

[Edit Style Sheet, lines 57-60, condensed]

function assignStyles() {     var styleName = this.name;     var styleValue = this.value; 


In addition to these change events, the script also needs a few extra event handlers. The first two fieldsthe select that allows the user to pick a rule and the Restore Defaults buttonneed their own event handlers, obviously a change and a click event, respectively:

{Edit Style Sheet, lines 30-31]

document.getElementById('selectors').onchange = assignRule; document.getElementById('restoreDefaults').onclick = restoreDefaults; 


Finally, the two Pick color links need their own click events to trigger the color picker:

[Edit Style Sheet, lines 39-42]

var links = document.getElementsByTagName('a'); for (var i=0;i<links.length;i++) {     if (links[i].className != 'colorPicker') continue;     links[i].onclick = placeColorPicker; 


Sandwich Picker

Sandwich Picker needs keyboard events for the Search box and every individual sandwich order box, and click events on all Order and Trash buttons. Setting the first event is very simple:

[Sandwich Picker, line 54]

document.getElementById('searchField').onkeyup = searchSandwich; 


The script sets the other events during the general loop through all the sandwiches. The sandwich order input box is already there, but the script creates the Order and Trash links by cloning templates (we'll discuss this in detail in 8E). One annoying feature of the cloneNode() method is that it does not clone event handlers, so I have to set them manually on every clone:

[Sandwich Picker, lines 45, 48 and 50]

searchField.onkeyup = orderSandwich; extraLink.onclick = removeSandwich; extraLink2.onclick = moveToOrderTable; 


All functions use the this keyword to find the element the user has clicked on or entered data in.

XMLHTTP Speed Meter

XMLHTTP Speed Meter also needs a key event on form fields. When the user changes his postal code or house number, the script should run. In addition, the user might press Enter to submit the form, and in that case the script should also run. Therefore, we also need a submit event:

[XMLHTTP Speed Meter, lines 24-29]

var formFields = document.getElementsByTagName('input'); for (var i=0;i<formFields.length;i++) {     if (formFields[i].type != 'text') continue;     formFields[i].onkeyup = initSendData; } document.forms[0].onsubmit = initSendData; 


Since the script has to read out both of the form fields (postal code and house number), it uses neither the this keyword nor the event target: it just manually searches for its data:

[XMLHTTP Speed Meter, lines 41-43]

function sendData() {    var postCode = document.getElementById('postcode').value;    var number = document.getElementById('huisnummer').value; 


Site Survey

Site Survey is a special case. It is the single example script that does not directly respond to user actions. Instead, it stores the URLs of the pages the user visits. Therefore, it has to be notified when the user goes to a new page, and to do that it needs either the load or the unload event.

An important job of Site Survey is keeping open communication with the main window, as we discussed in 6B. To do this, the script must be notified when the user leaves a page. It then starts up a script that tries to re-establish communication with the main window.

In order to run the communication script, the page needs access to the popup, and that's impossible onload, since at that time the communication has not yet been restored. Therefore, this job needs the unload event, and to keep things simple I use the same event for storing the URLs. ST_exit() handles both tasks.

Site Survey uses addEventSimple() since it's a module that can be added to any site and it shouldn't disturb the event handlers of the host site:

[Site Survey/survey.js, line 43]

addEventSimple(window,"unload",ST_exit); 


In addition, Site Survey needs a click event to open the popup in the first place. Since the user may click anywhere, I use a document-wide click event:

[Site Survey/survey.js, line 56]

addEventSimple(document,"click",ST_openPopup); 




ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net