reptile7's JavaScript blog
Thursday, March 11, 2010
 
Input Hopscotch
Blog Entry #173

We continue today our run through HTML Goodies' "JavaScript Form Scripts" series of tutorials. In this post we'll take up the third "JavaScript Form Scripts" tutorial, "Jump Focus with Form Elements". "Jump Focus with Form Elements" presents a script whose effect will probably be familiar to you if you've ever entered a phone number or Social Security number into a Web form: given a form containing a series of text fields, the script automatically moves the user from field to field upon entering a specific number of characters into each field. The "Jump Focus with Form Elements" script is posted here and is reproduced in the div below for your convenience:

<!-- Paste the JavaScript code between the HEAD Tags --> 

<head>

<script type="text/javascript">

// This code makes the jump from textbox one to textbox two
function check( )
{
var letters = document.joe.burns.value.length + 1;
if (letters <= 4)
{ document.joe.burns.focus( ); }
else
{ document.joe.tammy.focus( ); }
}

// This code makes the jump from textbox two to text box three
function check2( )
{
var letters2 = document.joe.tammy.value.length + 1;
if (letters2 <= 4)
{ document.joe.tammy.focus( ); }
else
{ document.joe.chloe.focus( ); }
}

// This code makes the jump from textbox three to textbox four
function check3( )
{
var letters3 = document.joe.chloe.value.length + 1;
if (letters3 <= 4)
{ document.joe.chloe.focus( ); }
else
{ document.joe.mardi.focus( ); }
}

// This code makes the jump from textbox four to the submit button
function check4( )
{
var letters4 = document.joe.mardi.value.length + 1;
if (letters4 <= 4)
{ document.joe.mardi.focus( ); }
else
{ document.joe.go.focus( ); }
}
</script>

</head>

<!-- The onLoad in the BODY tag puts focus in the first textbox -->
<body bgcolor="#ffffff" onload="document.joe.burns.focus( );">

<!-- This is the form -->
<form name="joe" action="">
<input type="text" name="burns" size="10" maxlength="4" onkeyup="check( );" /><br />
<input type="text" name="tammy" size="10" maxlength="4" onkeyup="check2( );" /><br />
<input type="text" name="chloe" size="10" maxlength="4" onkeyup="check3( );" /><br />
<input type="text" name="mardi" size="10" maxlength="4" onkeyup="check4( );" /><br />
<input type="submit" value="Click to Send" name="go" />
</form>

The script serves as a showcase for the onkeyup event handler. onkeyup was not an event handler for the text object in classical JavaScript but was picked up by the W3C for HTML 4 and now may be used with most elements. (For another onkeyup application, check out the lowercase-to-uppercase demo in the "Other 'After-the-Fact' Event Handlers" section of Blog Entry #24.)

Joe provides a script demo in the tutorial's "The Effect" subsection - we'll roll out our own demo later.

Overview

Joe applies his code to a form named joe and comprising four text fields plus a submit button; in source order, the text fields and submit button are respectively named burns, tammy, chloe, mardi, and go. The action begins with the body element start-tag

<body bgcolor="#ffffff" onload="document.joe.burns.focus( );">

whose onload attribute initially places focus in the burns field.

<input type="text" name="burns" size="10" maxlength="4" onkeyup="check( );" />

In his demo, Joe prepends a Put in Four Characters: string to the burns field and And Again: strings to the other fields. (This text does not appear in the code on the jumpfocuscode.html page - it's up to you to add your own field labels.) So, we type some characters in the burns field. As we do so, each keyup event calls the check( ) function in the script's script element:

function check( ) {
var letters = document.joe.burns.value.length + 1;
if (letters <= 4) document.joe.burns.focus( );
else document.joe.tammy.focus( ); }


In each run of the check( ) function, the length of the burns value (what we enter into the field) is indirectly compared to 4, the number of characters we want the field to hold; focus remains with the burns field if that length is 3 or less but shifts to the tammy field when the length reaches 4. Contra the discussion in the tutorial's "From Box One to Box Two" section, the check( ) function would be easier to follow if document.joe.burns.value.length were not incremented and the if declaration employed a simple less-than (not a less-than-or-equal-to) comparison, i.e.,

var letters = document.joe.burns.value.length;
if (letters < 4) document.joe.burns.focus( );


Anyway, this pattern repeats itself for the rest of the form. Entering characters into the tammy field calls an analogous check2( ) function that either keeps focus with the tammy field or shifts it to the chloe field depending on the number of tammy characters; entering characters into the chloe field calls a check3( ) function that keeps focus with the chloe field or shifts it to the mardi field depending on the number of chloe characters; and so on.

value validation, take one

The maxlength="4" attribute of each input element prevents the user from entering more than four characters into each joe field. But as the script stands, there's nothing stopping the user from entering fewer than four characters into a given field and then moving to another field via the mouse cursor or the tab key. Is there some way we can require the user to enter four characters into each field? In theory, this can be straightforwardly accomplished via a function expression of the following form:

document.joe.burns.onblur = function( ) {
if (document.joe.burns.value.length < 4) {
window.alert("Four characters, please.");
document.joe.burns.focus( ); } }


The above code is designed to
(a) notify the user via a Four characters, please alert( ) message and then
(b) return focus to the burns field
if the user deliberately blurs the burns field when its value.length is less than 4. In practice, here's how these expressions (four total, one for each field) play out with the various OS X GUI browsers on my computer:

• With Opera, they work very nicely if they are respectively nested in the corresponding onkeyup-triggered functions, but they gave rise to infinite alert( )-blur loops when I tested them in a separate script element following the joe form.

• With Safari and Chrome, blurring a value.length<4 field usually generates two alert( ) messages but focus is subsequently returned to the field, regardless of where the expressions are placed.

• With Firefox and Camino, blurring a value.length<4 field generates one alert( ) message but focus is not returned to the field, regardless of where the expressions are placed.

• An attempt to sort out the Safari/Chrome/Firefox/Camino behavior by using the window.setTimeout( ) method to delay the execution of the expressions' focus( ) commands also gave rise to infinite alert( )-blur loops.

• I was originally going to leave MSIE out of this discussion - MSIE 5.2.3 for Mac OS X is not what I would call an old browser but it isn't really a modern browser either - but for the record, MSIE gives the desired effect when the expressions are nested in the onkeyup functions and focus( ) command execution is delayed, but behaves problematically under other conditions (I'll spare you the details).

So, validating 'as we go along' is not so straightforward after all. However, we can at least carry out easily a summary validation at the submit-the-form stage, and we'll do that in the "Demo" section below.

Four into one

In the tutorial's "The Full Code" section, Joe discloses that the check( )/check2( )/check3( )/check4( ) functions were originally a single function:
Here's where I often catch heck from experienced programmers. Much of the code I offer on HTML Goodies is wordy. Before you write to tell me, yes, I know this can be done with a single function. In fact, Jim's code was a single function. The code I am offering here breaks out each function individually.

I only do that because it makes the code simpler and allows a less-experienced programmer to grasp the concept. That's all...
Commenter Matt attempts to fill in the blanks:
Instead of writing methods for each instance of the text inputs, write one parameterized method.

function che***(limit, currentId, nextId) {
var element = document.getElementById(currentId);

if (element.value.length >= limit) {
document.getElementById(nextId).focus( ); } }


Then the onKeyUp event for [the burns input] can be "javascript:che***(4, this.id, 'tammy');" and similar on down the line. This will save you a bunch of coding.
Matt is on the right track, but his code makes a fatal mistake: name and id attribute values cannot be used interchangeably in most situations*, and this is the case here. The joe input elements do not have id attributes; as a result,
(a) this.id, the second che***( ) function call parameter, evaluates to an empty string, and
(b) var element = document.getElementById(currentId);, the first che***( ) statement, will return null (which cannot be used as an object in hierarchy expressions, in case you were wondering) - obviously not what we want.

On the other hand, Matt's che***( ) function can be used verbatim if we
(1) equip the joe input elements with id attributes - say, id="input0", id="input1", etc. - and then
(2) set the nth input element's third che***( ) function call parameter to the n+1th input element's id attribute value, i.e.:

<input id="input0" type="text" name="burns" size="10" maxlength="4" onkeyup="che***(4, this.id, 'input1');" /><br />
<input id="input1" type="text" name="tammy" size="10" maxlength="4" onkeyup="che***(4, this.id, 'input2');" /><br />
<!-- Etc. The name attributes should be left in place, as they are required for <input type="text"> elements. -->


*name/id attribute values can be used interchangeably in old-school associative object referencing - for example, <img id="imageID" name="imageName" src=URI alt=""> can be referenced with document.images["imageID"] or document.images["imageName"] - but to the best of my knowledge, that's as far as their overlap goes.

Another point about Matt's code: the onkeyup attribute values should not be formulated as JavaScript URLs - the javascript: protocol can and should be removed.

We can simplify Matt's che***( ) function via the elements[ ] property of the form object. If each input element feeds to che***( ) its elements[ ] index as arguments[0], its value.length as arguments[1], and its value.length limit as arguments[2], e.g.,

<input type="text" name="burns" size="10" maxlength="4" onkeyup="che***(0, this.value.length, 4);" />

then the che***( ) function can be recast as:

function che***(fieldIndex, fieldLength, limit) {
if (fieldLength >= limit)
document.joe.elements[fieldIndex + 1].focus( ); }


Demo

The div below holds a demo that asks the user to enter a 10-digit phone number into a form comprising three text fields, a submit button, and a reset button. The form will "jump focus" if the user enters three digits, three digits, and four digits into the first field, second field, and third field, respectively; the form will not jump focus if a given field contains one or more non-digit characters, but the user can still move to the next field via the mouse cursor or tab key. The button can be clicked at any time; it will pop up an apposite alert( ) message reflecting whether or not the user has entered a 123-456-7890-format phone number into the form. Feel free to try the demo with any combination/number of digit and non-digit characters.

Please enter your 10-digit phone number:

- -




The jump-focus effect is achieved via a jump( ) function:

function jump(fieldIndex, fieldValue, limit) {
if (fieldValue.length >= limit && /^\d{3,4}$/.test(fieldValue))
document.forms["joe"].elements[fieldIndex + 1].focus( ); }


The regular expression-based /^\d{3,4}$/.test(fieldValue) if subcondition enables the jump-focus effect if a field contains three or four digits, and prevents it otherwise. Go here for a description of the test( ) method of the core JavaScript RegExp object.

The user's phone number is validated at the form submission stage via a digitCheck( ) function:

<form id="joe" onsubmit="return digitCheck( );" onreset="document.forms['joe'].elements[0].focus( );"> ... </form>
<script type="text/javascript">
function digitCheck( ) {
var phonenumber = document.forms["joe"].elements[0].value + document.forms["joe"].elements[1].value + document.forms["joe"].elements[2].value;
if (/^\d{10}$/.test(phonenumber)) {
window.alert("Thank you!"); return false; }
else {
window.alert("Your phone number does not comprise ten digit characters. Get it sorted out, will you?"); return false; } }
</script>


The form's field values are concatenated and the resulting string is given the identifier phonenumber. phonenumber is then compared to a /^\d{10}$/ regular expression signifying a ten-digit character sequence; digitCheck( ) pops up a Thank you! message if they match and a Your phone number does not comprise ten digit characters. Get it sorted out, will you? message if they don't. In both cases digitCheck( ) returns false so as to cancel the form's submission (this is a demo, after all).

We'll see more onkeyup action in "Pure Magic Form Script", the fourth "JavaScript Form Scripts" tutorial, whose scripts we'll dissect in the next entry.

reptile7

Comments:
Hi. Nice Try. Keep it up.
I also request you to visit my blog:
Notebooks Examined
 
Post a Comment

<< Home

Powered by Blogger

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