reptile7's JavaScript blog
Monday, June 11, 2012
Accept to Enable
Blog Entry #254

Today's post will discuss HTML Goodies' "How To Use JavaScript To Ensure Users Agree Before Posting" tutorial, which was authored by Scott Clark. The "Ensure Users Agree" tutorial offers a script that respectively enables or disables the controls of a form depending on whether the user accepts or does not accept the Webmaster's terms of service via checking or not checking an "I Accept" checkbox. The "Ensure Users Agree" script is very slightly adapted from a "[Enable] form fields if checkbox is checked" script crafted by savvykms, a.k.a. Savvy.


The "Ensure Users Agree" script works with a form holding four controls.
elements[0] is the gatekeeper checkbox.
elements[1] and elements[2] are Name: and E-Mail: text fields, respectively.
elements[3] is a Post: textarea field.
Here's the form code:

<form name="myform">
<p>To accept the license terms for posting, click the checkbox below:<p>
<input type="checkbox" name="mycheck" onclick="toggleform('document.myform', 'mycheck', 'pname,pemail,ptext');"> I Accept</p>
<td><b>Name:</b></td><td><input type="textbox" id="pname" name="pname" disabled="true"></td>
<td><b>E-Mail:</b></td><td><input type="textbox" name="pemail" disabled="true"></td>
<td><b>Post:</b></td><td><textarea name="ptext" disabled="true"></textarea></td>

HTML notes

(1) The text inputs and their preceding text strings are placed in a table in order to horizontally align the left edges of the input boxes; if you don't care about this, then you can throw out the table markup (there's no table in Savvy's original code).

(2) The text inputs are initially disabled via a disabled="true" attribute. In fact, true is not a legal value for the HTML disabled attribute: the correct formulation should be either disabled or disabled="disabled". However, the "Notes on invalid documents" section of the HTML 4.01 Specification recommends:
If a user agent encounters an attribute value it doesn't recognize, it should use the default attribute value.
Accordingly, the browser replaces true with disabled, the #IMPLIED default value for the disabled attribute, and disables the text inputs.

(3) Similarly, textbox is not a legal value for the type attribute of the input element; the value we really want is text, which fortunately is the attribute's default value, and the browser replaces textbox with text per the preceding note.

(4) Semantically, the Name:, E-Mail:, and Post: strings are labels, so we should mark them up that way. If we keep the layout table, then the label strings and the text inputs will have to be explicitly associated via label for attributes and input id attributes, e.g.:

label { font-weight: bold; }
<td><label for="input1">E-Mail:</label></td>
<td><input type="text" id="input1" name="pemail" disabled></td>

If we ditch the table, then we can associate them implicitly:

<label>E-Mail: <input type="text" name="pemail" disabled></label>

The JavaScript

Checking the mycheck checkbox calls a toggleform( ) function and passes thereto three arguments.
arguments[0] is document.myform, which refers to the parent form.
arguments[1] is mycheck, which refers to the checkbox itself.
arguments[2] is pname,pemail,ptext, a comma-delimited list of the names of the form controls we want to enable.
These arguments are passed as strings (not as reference expressions) and are respectively given the identifiers formstr, chkobstr, and obstr.

function toggleform(formstr, chkobstr, obstr) { ... }

The toggleform( ) function first gets the checked status of the mycheck checkbox via the following statement:

var checked = eval(formstr + "." + chkobstr + ".checked");

formstr, ., chkobstr, and .checked are concatenated and the resulting document.myform.mycheck.checked expression string is fed to the top-level eval( ) function, which evaluates the string to give true, which is assigned to a checked variable.

Next, the obstr string is split( ) to give an obs array of control name substrings.

var obs = obstr.split(",");

A for loop subsequently prepends formstr and . to each obs substring.

for (i = 0; i < obs.length; i++) { obs[i] = formstr + "." + obs[i]; }

We now have an array of stringified references for the disabled text inputs, as though we had coded:

obs = ["document.myform.pname", "document.myform.pemail", "document.myform.ptext"];

After the loop an if statement tests if checked is equal to false:

if (checked == false) { ... } // Alternatively: if (!checked) { ... }

It isn't, so control passes to an accompanying else clause:

else {
    for (i = 0; i < obs.length; i++) { enableob(obs[i]); }
    document.getElementById("pname").focus( ); }

The else clause sequentially feeds the obs strings to an enableob( ) function.

function enableob(o) { eval(o + ".disabled = false"); }

A .disabled = false string is appended to each obs/o string; the resulting document.myform.controlName.disabled = false statement string is executed by the eval( ) function to enable the corresponding control. Unlike the HTML disabled attribute, the DOM disabled attribute (its HTMLInputElement interface listing is here) in a JavaScript context takes a literal true or false (1 or 0 respectively will also work).

Once the text inputs are enabled, focus is given to the Name:/pname field by the document.getElementById("pname").focus( ); command.

Redisabling the text inputs

We noted two entries ago that both checking and unchecking a checkbox dispatch a click event. Per the foregoing discussion:
(1) Unchecking the mycheck checkbox calls the toggleform( ) function and passes it document.myform, mycheck, and pname,pemail,ptext arguments.
(2) The checkbox's checked status is read and found to be false.
(3) The obstr argument is split( ) into control name substrings, which are converted into stringified object references.
The aforementioned if (checked == false) statement kicks in:

if (checked == false) {
    for (i = 0; i < obs.length; i++) { disableob(obs[i]); } }
function disableob(o) { eval(o + ".disabled = true"); }

/* enableob( ) and disableob( ) both appear before toggleform( ) in the source, but the function order here is immaterial. */

Complementing the enableob( ) function is a disableob( ) function that disables each text input by setting its DOM disabled attribute to true.

That wraps up our script deconstruction. In the following post I'll have a few more things to say about the "Ensure Users Agree" tutorial - in particular, I'll show you a much simpler way to code the toggleform( )/enableob( )/disableob( ) functionality - and then I'll briefly outline what we'll be doing next.

Comments: Post a Comment

<< Home

Powered by Blogger

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