Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning JavaScript With DOM Scripting And Ajax - From Novice To Professional (2006)

.pdf
Скачиваний:
80
Добавлен:
17.08.2013
Размер:
17.27 Mб
Скачать

276

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

One special element type is the select box, which comes with a collection of its own, a property called options—but more on this later. Each element has a range of methods that is also dependent on the type of element. None of these methods expect any parameters.

blur(): Takes the focus of the user agent away from the element (all elements)

focus(): Puts the focus of the user agent on the element (all elements)

click(): Simulates the user clicking the element (buttons, check boxes, file upload fields, Reset and Submit buttons)

select(): Selects and highlights the text content of the element (password fields, text fields, and text areas)

Note Notice that click() seems at first a bit odd, but can be really helpful if you work on web applications, and the middle tier of your development environment does things to the submitting process of forms like Java Spring and .NET do. This is not a JS beginner’s environment though, and so falls outside the scope of this book.

HTML Attributes Not Contained in the Elements Collection

In addition to the properties of the elements collection, you can read and set (browser settings permitting of course) the attributes of the element in question once you have reached it via the forms and elements collections. For example, you could change the size of a text field by changing its cols and rows attributes:

var myTextBox = document.forms[0].elements[2]; if ( myTextBox.type == 'textarea' ){

myTextBox.rows = 10; myTextBox.cols = 30;

}

Tip Notice that it is a good idea to check the type of the element you are manipulating before trying to set attributes on it, in case they are not available for this element. A SELECT element, for example, does not have cols or rows attributes.

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

277

Globally Supported Properties

All form elements initially supported the type, name, form, and value properties. One recent change is that file upload fields do not support setting value any longer, as it would allow a malicious scripter to inject her own files to upload to your server when a user on an infected computer uploads something.

Using form can be quite handy to reach the parent form when you access an element directly via DOM methods. For example, if you access the e-mail field in the example form via the DOM:

var mail = document.getElementById( 'email' );

You can reach the form to change one of its properties or submit it either via mail.form or via mail.parentNode.parentNode.parentNode.

exampleForm.html (excerpt)

<form method="post" action="send.php"> <fieldset>

[... code snipped ...]

<p><input type="text" id="email" value="you@example.com" name="email" /></p> </fieldset>

[... code snipped ...] </form>

Depending how deep the element is nested in the form, using form can save you a lot of trouble counting the nodes and actually makes the script easier to maintain because you are independent of the HTML. If you want to use node traversal exclusively, you could also use a recursive loop that checks the nodeName of the parent node to achieve the same independence of the HTML markup:

var mail = document.getElementById( 'email' ); parentForm = mail.parentNode;

while( parentForm.nodeName.toLowerCase() != 'form' ) { parentForm = parentForm.parentNode;

}

While this is independent of the user agent, it might be overkill in most web sites, as you can assume that most browsers do offer the forms and elements collections (you can, of course, test for them to make sure). Using the forms and elements collections also means you support non–DOM-2 browsers like Netscape 4 or MSIE 4.

278

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

Using blur() and focus()

You can use blur() to take the user agent’s focus away from an element or focus() to set it. The danger of this is that blur() does not take any target it should set the focus to, which means that user agents might focus on the next element, the location bar of the browser, or whatever else they please. For a sighted user with a mouse, this is not much of an issue; however, blind users relying on assistive technology or keyboard users will have a problem finding their way around the document again.

You might encounter something like this when you look through the code of some web sites:

<a href="#" onclick="dothings();" onfocus="this.blur()">Home</a>

Developers used to do this to stop browsers from showing a blue box (MSIE on Mac) or a dotted border (MSIE/Mozilla on PC) around the current link. This is a very bad idea, as a keyboard user wouldn’t know which element she is currently able to reach when hitting Enter.

There are some legitimate reasons to use focus(); however, in most cases, it is not a good idea to alter the automatic order of form entry. Not every user can see the form, and even users who can see might not look at it.

Especially in longer forms that expect a lot of different data to enter, you’ll find that people don’t look on the screen, but touch-type as they read the data from a printout or their credit card, passports, and so on. It is pretty frustrating to check the form in between and realize you didn’t fill out the right fields or you are still stuck at an error message that popped up earlier.

Text Fields, Text Areas, Hidden and Password Fields

Text fields, text areas, hidden fields, and password fields are probably the most common fields you will have to deal with, as they are the ones that users enter text content in.

In addition to the global form element properties, they also support the element properties value and defaultValue. The difference is that if a user changes the content of the element, it does change the value property but not the defaultValue. This also means that when you change the value of the field, the change is visible, but when you change the defaultValue, it isn’t. If you want to change the element’s default value and make it visible, you need to call the reset() method immediately afterwards. In the example document, you have a default value on the e-mail field:

<p><input type="text" id="email" value="you@example.com" name="email" /></p>

You could read the value and the default value like this:

var mail= document.getElementById( 'email' ); alert( mail.defaultValue );

alert( mail.value );

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

279

When the user hasn’t changed anything on the field, both values would be you@example.com. However, if the user were to enter me@otherexample.com in the field, the two values become different.

It is tricky to find an example for defaultValue that does not mean you have to do something with JavaScript that should be the job of the back end. One example could be to test whether the current domain is German and change the default of the e-mail field to a German e-mail address. The following code should be executed when the document has loaded:

var mail = document.getElementById( 'email' );

if( window.location.host.indexOf( '.de' ) != -1 ) { mail.defaultValue = 'email@adresse.de'; mail.form.reset();

}

Notice that you need to call reset() to make the change visible. You can see the change happening in exampleFormGermanPreset.html (we cheated there to make it visible by excluding the host test).

Note For TEXTAREA elements, you read and write the value and defaultValue just like for any other form text element. However, the HTML tag has no value attribute—the initial and the changed value is the text contained in between the opening and closing tag.

Text elements allow for a method called select(), which highlights all the text inside them to ease copying and pasting of text examples. This is often seen as a feature in webzines or online documentation systems.

Check Boxes

Check boxes are a great way to offer an unambiguous “yes” or “no” choice. They are very easy to read out on the server side (the form sends the name of the box with the value of the check box if there is one or “on” if there is none, and doesn’t send the name at all when the user hasn’t checked the box) and much easier to use than, for example, a radio button group or a select box with “yes” and “no” options.

In addition to the global element properties described earlier, check boxes have the properties checked and defaultChecked, both Boolean values indicating whether the option was chosen. You can read and write both of these properties, but you need to reset the form to make changes to defaultChecked visible.

One common use of JavaScript in connection with check boxes is offering the user the opportunity to check all check boxes or reverse the choices made in a lot of check boxes, one example being in web-based e-mail systems, as shown in Figure 7-6. The logic of these functions is pretty simple: you loop through all elements, test the type of each one, and change the checked attribute accordingly. You can see a demo of this in exampleFormCheckboxes.html.

280

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

Figure 7-6. Bulk changing of check boxes via JavaScript

There are three buttons in the example that call the same function—changeBoxes()— when the user clicks them. Each button supplies a different numeric value for the function’s sole parameter—1 for select all, -1 for invert, and 0 for select none.

exampleFormCheckboxes.html (excerpt)

<input type="button" onclick="changeBoxes(1)" value="select all" />

<input type="button" onclick="changeBoxes(-1)" value="invert selection" /> <input type="button" onclick="changeBoxes(0)" value="select none" />

This allows you to keep the function to change the check boxes simple. Simply loop through all the elements in the first form found in the page. If the element type is not checkbox, continue the loop without executing the rest of it.

If the element is a check box, determine whether action is smaller than 0 and reverse the check box state by changing checked to false when it is true and vice versa. If the action is 0 or larger, simply set the checked property to the value of action.

exampleFormCheckboxes.html (excerpt)

function changeBoxes( action ) { var f = document.forms[0];

var elms = f.elements;

for( var i = 0; i < elms.length; i++ ) {

if( elms[i].type != 'checkbox' ){ continue; } if( action < 0 ){

elms[i].checked = elms[i].checked ? 0 : 1;

}else {

elms[i].checked = action;

}

}

}

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

281

If this is confusing, remember that the checked property is a Boolean value. This means when it is true or 1, it is checked, and when it is false or 0, it is unchecked. If you were to use only the true or false keywords, you’d have to add another case in the else condition (via the ternary notation in this case):

function changeBoxes(action) { var f = document.forms[0]; var elms = f.elements;

for( var i = 0; i < elms.length; i++ ){

if( elms[i].type != 'checkbox' ){ continue; } if( action < 0 ){

elms[i].checked = elms[i].checked ? false : true;

}else {

elms[i].checked = action == 1 ? true : false;

}

}

}

Using the ternary operator, you could even reduce the whole check box logic part of the script to one line:

function changeBoxes( action ) { var f = document.forms[0];

var elms = f.elements;

for( var i = 0; i < elms.length; i++ ){

if( elms[i].type != 'checkbox' ){ continue; }

elms[i].checked = action < 0 ? (elms[i].checked ? 0 : 1 ) : action;

}

}

As a lot of complex form code is not necessarily created by client-side-oriented developers, there is a high chance you might encounter constructs like this, which is why we took the opportunity here to show it to you.

Radio Buttons

Radio buttons have their name because they look like the dials on old radios, in case you wondered. They act like check boxes, with the difference being that they belong to one group with the same name, and the user can only choose one exclusively. Radio buttons are very easy to use for mouse and keyboard users alike, and they are a good replacement for short select boxes in case you ever run into problems using a select box.

They have the same Boolean checked and defaultChecked properties as check boxes, but will automatically set the checked property of the other choices to false when you set one. Again, you can read and write both checked and defaultChecked, and you need to reset the form

282

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

to make changes to defaultChecked appear visually. As the example HTML only has a radio group of two options, let’s choose a different example:

exampleFormRadioGroup.html (excerpt)

<form method="post" action="send.php"> <fieldset>

<legend>Step 1 of 3 - Your favourite Character </legend> <p>

<input type="radio" name="character" id="charC" value="Calvin" checked="checked" />

<label for="charC">Calvin</label> </p>

<p>

<input type="radio" name="character" id="charH" value="Hobbes" />

<label for="charH">Hobbes</label>

</p>

<p>

<input type="radio" name="character" id="charSd" value="Susie Derkins" />

<label for="charSd">Susie Derkins</label> </p>

<p>

<input type="radio" name="character" id="charS" value="Spaceman Spiff" />

<label for="charS">Spaceman Spiff</label> </p>

<p>

<input type="radio" name="character" id="charSm" value="Stupendous Man" />

<label for="charSm">Stupendous Man</label> </p>

</fieldset>

<p class="submit"><input type="submit" value="Next Step" /></p> </form>

Note This is also a good opportunity to show the difference between name and id. While a group of radio buttons all share the same name (in this case character), they each must get a unique id to allow the labels to be connected with them. Labels are not only handy for assistive technology like screen readers, but also make the form much easier to use, as users can click the names next to the check boxes to select them.

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

283

The demo HTML includes some buttons to show the output of the JavaScript; you can test it out by opening it in a browser. The script shows how you can deal with radio buttons:

formRadioGroup.js

function setChoice( n ) { var f = document.forms[0];

f.character[n].checked = true;

}

function getChoice() {

var f = document.forms[0];

var choices = f.elements.character; for(var i = 0; i < choices.length; i++ ){

if( choices[i].checked ){ break; }

}

alert( 'Favourite Character is: ' + choices[i].value );

}

You can access the radio button group as an array with the shared name (in this case character). Setting an option of a radio group is pretty straightforward: the setChoice() function takes a number as a parameter (n), reads the first form (forms[0]), and sets the checked property of the n-th character item to true.

formRadioGroup.js (excerpt)

function setChoice( n ) { var f = document.forms[0];

f.character[n].checked = true;

}

If you click the “set choice to Hobbes” button in the example, you see that the highlighted radio button changes as shown in Figure 7-7.

Figure 7-7. Changing the selected option in a radio group

284

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

Reading the currently selected choice is just as easy: you select the first form, store the characters list in a new variable called choices, and loop through it. Then, you test the checked property of each of the elements in the array and break the loop when you find one that returns true. This is the currently selected radio choice: there can be only one that is selected in any group of radio buttons sharing the same name.

formRadioGroup.js (continued)

function getChoice() {

var f = document.forms[0];

var choices = f.elements.character;

for( var i = 0; i < choices.length; i++ ) { if( choices[i].checked ){ break; }

}

alert( 'Favourite Character is: ' + choices[i].value );

}

Buttons

There are three kinds of buttons in HTML: two that work without scripting, and one that was included in the specifications to work only in conjunction with scripts.

Authors may create three types of buttons:

submit buttons: When activated, a submit button submits a form. A form may contain more than one submit button.

reset buttons: When activated, a reset button resets all controls to their initial values.

push buttons: Push buttons have no default behavior. Each push button may have client-side scripts associated with the element’s event attributes. When an event occurs (e.g., the user presses the button, releases it, etc.), the associated script is triggered.

http://www.w3.org/TR/REC-html40/interact/forms.html#buttons

This makes “push buttons”—which are either the input-type button or the button ele- ment—the perfect trigger element for JavaScript-only functionality.

The Reset and Submit buttons, on the other hand, are very important parts of the form and shouldn’t be tampered with unless you really have a good reason for the change. One recurring request is to change the value or the state of the Submit button when the form was submitted to prevent impatient users from clicking the button twice. You could do this via a click handler; however, the better option is to use a submit handler on the form, as this would also trigger the change when the form is submitted via the Enter key. Figure 7-8 shows how this might look.

C H A P T E R 7 J A V A S C R I P T A N D U S E R I N T E R A C T I O N : N A V I G A T I O N A N D F O R M S

285

Figure 7-8. Changing the style and text content of a Submit button when the form gets sent

All you need to do to achieve this functionality is assign an event handler to the window that calls an init() function and another one that calls a change() function when the form is submitted.

This function loops through all the form elements (after retrieving the form via getTarget()) and checks whether the element is an image or a Submit button. If this is the case, it disables the button via the disabled attribute and changes the button value to Please wait:

exampleChangeSubmitButton.html (excerpt)

submitChange = {

init : function() {

DOMhelp.addEvent( document.forms[0], 'submit', submitChange.change,false);

},

change : function( e ){

var t = DOMhelp.getTarget( e );

for( var i = 0; i < t.elements.length; i++ ){

if( !/submit|image/.test( t.elements[i].type ) ) { continue; } t.elements[i].disabled = true;

t.elements[i].value = 'Please wait...';

}

}

}

DOMhelp.addEvent( window, 'load', submitChange.init ,false );

An image button defined via <input type="image"> acts like a Submit button, the only difference being that it doesn’t submit its name to the back end but two sets of name-value pairs, consisting of the original name followed by .x and .y and the coordinates the user clicked as the values. That way you can perform different actions depending on where the button was clicked. This information is not readable via JavaScript, only on the back end.