JavaScript & HTML Forms, Inputs, & URL parameters

This is not intended as a complete tutorial to using forms in HTML, indeed it is assumed here that the programmer already has a basic knowledge of them.  However, there are a number of subtle aspects to their use ranging from the genuinely misleading and confusing to the merely cosmetic, and these in turn lead to a common set of programming problems which the script associated with this page, and other major forms on this site, attempts to overcome.  To download the script to your PC, <Right-Click> Forms.js and choose Save As.

Common Problems Programming Forms

This is not an exhaustive list, but I think most of the ones that I've encountered are here:

Documentation For "Forms.js"

Besides one or two global variables and functions, the script defines a number of types of 'background' data object each corresponding to a particular type of form element.  All the objects and functions intended for normal public use are documented here.  Others exist for the use of which you'll have to read and understand the code, but this is only likely to be required if you wish to extend the Object hierarchy.

Queue

An object forming a queue of listener functions.

By default the script creates a global queue called globalListeners.  It is usually better to arrange that intensive processes dependent on fields in a form, such as updating a map, occur via global listeners rather than listeners on the particular fields affecting them, because then they will occur just once for each user change in the form, even if that change cascades, via listeners set on the changed value, throughout the form causing other changes in its wake.  The mechanism by which this occurs is described under StrValue.runListen().

Constructor parameters:  None

addListen()

Add a listener to the queue, ignoring duplicates of existing listeners.

Parameters:

fListenerThe listener function to be added.

 

delListen()

Delete a listener from the queue.

Parameters:

fListenerThe listener function to be deleted.

 

runListen()

Run the queue's listeners, in the order that they were originally added to the queue.

Parameters:  None, though each listener called receives a single parameter referring to this object calling the listener.

 

StrValue

String input.  This example demonstrates the upper case flag.  The case flag is optional.


 

 

 

Inherits from Queue.

Constructor parameters:

aValueInitial value for the string
anElement ID of an ancestor, typically a <div>, containing the <input>.  Something like the following arrangement is expected (strictly, excepting radio buttons, actual containment is only necessary for dynamic form layout, where user choice can cause fields to be shown or hidden).  Note particularly the IDs  -  the container ID can start with any first letter except i, which must start the form element's ID, or l, similarly reserved for the label's ID, and the rest of their IDs must match.  For this example, this parameter would be "dInp0"If there is a name defined for the form element, it should be the same as the ID.

<div id="dInp0" title="[field help]">
<div id="lInp0"><label for="iInp0">Field Label</label></div>
<input type="text" id="iInp0" name="iInp0" value=""
onchange="this.oData.setValue();" />
/>
</div>

caseFlag"L"Convert to lower case
"U"Convert to upper case
undefinedNo case conversion
trimFlag Trim leading and trailing whitespace from user input.  Default true.

 

getValue()

Get the field value.

Parameters:  None

 

setValue()

Set the field value, updating its visible representation in the form element.

Parameters:

aValue An appropriate new value.  If this parameter is absent, it is assumed that the value in the form has been changed by the user, and this is used as the new value.

 

getElement()

Get the associated form element containing the visible representation of the field.

Parameters:  None

 

update()

Updates the form element for this field, hiding or showing it, setting it read only or not, giving it focus or not, depending on the parameters given.

Parameters:

srcVal A source field whose value is to be used in conjunction with other parameters.
shown Boolean for whether this field's element is to be shown or hidden.  If this parameter is not of boolean type, the required boolean is determined by comparing this parameter against srcVal.getValue().  Default is false.
active Boolean for whether this field's element is to be active or read only.  If this parameter is not of boolean type, the required boolean is determined by comparing this parameter against srcVal.getValue().  This parameter is ANDed with shown and its default value is shown.
focus Boolean for whether this field is the next to be given focus.  If this parameter is not of boolean type, the required boolean is determined by comparing this parameter against srcVal.getValue().  This parameter is ANDed with active and its default value is active.  If true, the next property of srcVal's element is set to be this field's element.
nextFocus Another element to be next to receive focus.  If focus is false, and this parameter is defined, the next property of srcVal's element is set to be this element.

 

runListen()

Called from setValue() if the new and old values are not equal.  Run the listeners associated with this value, and possibly any global ones:

  1. If global variable ancestor is free, grab it;
  2. Call own listeners;
  3. If ancestor, call globalListeners.runListen(), and release ancestor.

Parameters:  None

 

ChkValue

Checkbox input.


 

 

 

Inherits from StrValue.  If setValue() is given a non-boolean parameter, it will set the checkbox according to whether the parameter matches the checkbox value.

Constructor parameters:

aValueInitial value for the checkbox state
anElementID of the ancestor containing the <input>

 

setURLValue()

By default, check boxes only give rise to a URL parameter if selected, and then the value of the parameter is the value attribute of the <input>, rather than the more useful true or false of the checked state.  Thus, for a form where a checkbox is selected by default, but a user unchecks it, no URL parameter to recreate that choice would arise.  Before the form is submitted, this function sets the checked state into the field value, and then sets the checked state, giving rise to a parameter of the form name=[true|false].

Parameters:

aValueThe value for the URL parameter, default this.value.

 

RadValue

Radio button input.


 

 

 

 

 

Inherits from StrValue.  getValue() returns the value of the currently selected button.  setValue() must have a parameter matching a button's value to effect a change.

Constructor parameters:

aValueValue of button to be selected initially
anElement ID of the ancestor containing several <input>s of type ="radio".  The expected arrangement is similar to that for an <input> of type="text", with the addition of suitable values for the buttons and a parameter to each button's onchange event:

<div id="dRad" title="[field help]">
<div title="[button help]">
<div id="lRad1"><label for="iRad1">Button Label</label></div>
<input type="radio" id="iRad1" name="FieldName" value="R1"
onchange="this.oData.setValue(this.value);"
/>
</div>

</div>

 

IntValue

Integer input.  By default, integers display as decimal, but theRadix parameter can specify an alternative default radix for entry and display.  Further, for entering data, most of the known programming syntaxes for designating radix can be used to override this default.  However, % is ambiguous, signifying hexadecimal escapes in URLs, but in some other contexts signifying binary numbers.  Unless changed with thePCRadix, % signifies binary.

A binary example.  The default radix for input is 2 and the number is displayed in binary:


 

 

 

A decimal example.  The default radix for input is 10 and the number is displayed in decimal:


 

 

 

A hexadecimal example.  The default radix for input is 16 and the number is displayed in hexadecimal:


 

 

 

Inherits from StrValue through NumValue, an abstract object not intended to be instanced.

Constructor parameters:

aValueInitial value for the integer
anElementID of the ancestor containing the <input>
aMinMinimum allowable value, default -Number.MAX_VALUE
aMaxMaximum allowable value, default Number.MAX_VALUE
theRadixDefault radix, default 10
thePCRadixDefault radix for numbers flagged by %, default 2

 

FPValue

Floating point input.  Takes a floating point number and stores it at full javascript accuracy but can display it to a given number of decimal places.  Accepts input in scientific notation.


 

 

 

Inherits from StrValue through NumValue, an abstract object not intended to be instanced.

Constructor parameters:

aValueInitial value for the float
anElementID of the ancestor containing the <input>
theDecimalsNumber of decimal places for rounding display, default -1 no rounding
theURLDecimalsNumber of decimal places for rounding URL parameters, default theDecimals + 2
aMinMinimum allowable value, default -Number.MAX_VALUE
aMaxMaximum allowable value, default Number.MAX_VALUE

 

setURLValue()

Before the form is submitted, set into the field a value suitably rounded for a URL parameter.

Parameters:

aValueThe value for the URL parameter, default this.value.
decimalsThe number of decimals for rounding the URL parameter, default this.URLdecimals.

 

ConValue

ConValue input.  Stores a FP value in one unit but displays it in another, in this example the displayed and entry values are in miles, but the internal value is in kms.  This is useful for those situations where it is more convenient to perform calculations in one set of units, perhaps SI units, but site users might be more accustomed to other units, say British units.


 

 

 

Inherits from FPValue.  getValue() and setValue() get and set the internal value of the field, here kilometres, rather than the displayed value, here miles.

Constructor parameters:

aValueInitial value, in the units of storage rather than of display
anElementID of the ancestor containing the <input>
theConFactorThe conversion factor for displaying the value
theDecimalsNumber of decimal places for rounding display, default -1 no rounding
theURLDecimalsNumber of decimal places for rounding URL parameters, default theDecimals + 2
aMinMinimum allowable value, default -Number.MAX_VALUE
aMaxMaximum allowable value, default Number.MAX_VALUE

 

Angle

Angle input.  Inputs and displays an angle as either decimal degrees or degrees:minutes:seconds, including latitude and longitude notation, but stores it as a floating point number in radians.  This avoids the need to continually convert to radians before manipulating angles using trigonometric functions, while keeping the convenience of degrees for the user.


 

 

 

Inherits from ConValue.  getValue() and setValue() get and set the internal value of the field, radians, rather than the displayed value, degrees.

Constructor parameters:

aValueInitial value for the angle (in radians)
anElementID of the ancestor containing the <input>
theDecimalsNumber of decimal places for rounding display, default -1 no rounding.  If DMS is set:
-1No rounding
0Round to nearest degree
1,2Round to nearest minute
3,4Round to nearest second
> 4Round seconds to decimals - 4
theURLDecimalsNumber of decimal places for rounding URL parameters, default theDecimals + 2
aMinMinimum allowable value, default -Number.MAX_VALUE
aMaxMaximum allowable value, default Number.MAX_VALUE
dmsFlags whether to display angle as decimal degrees or degrees : minutes : seconds
falseDefault, decimal degrees
trueDegrees : Minutes : Seconds
[ <Positive Suffix>, <Negative Suffix> ]Degrees ° Minutes ' Seconds"<suffix>
for example ["N","S"]Degrees ° Minutes ' Seconds"<N or S>

 

setDMS()

Set whether to display angle as decimal degrees or degrees : minutes : seconds.

Parameters:

dmsAs per the constructor parameter.
getNormVal()

Get the Angle value as a number between 0 & 2π.

Parameters:  None

 

DTValue

Date and time input.

Dates and times are stored by JavaScript as the number of milliseconds since Midnight 01/01/1970.

WARNING:  Both input &/or output formats will be locale &/or browser dependent.  To get an idea of the unreliability this causes, here is output of the major Date functions in three of the most common browsers.  The 1st column lists the function being tested, the 2nd its output, the 3rd how that output is read back in by Date.parse().  Note that neither FF nor IE will read a standalone time correctly, while Opera, although it does so, adds today's date to the time, which, if you really wanted just the time to add to another date, would trash the calculation.  Also, because it uses the short date format for some functions, Opera cannot parse correctly the output of those functions for ambiguous dates (where day of the month ≤ 12) in locales having an output format DD/MM/YYYY, because Date.parse() expects MM/DD/YYYY:

Date FunctionResultResult of Date.parse( Result )
Firefox 3.5.3
toString()Wed Jun 01 2005 12:00:00 GMT+0100 (GMT Daylight Time)Wed Jun 01 2005 12:00:00 GMT+0100 (GMT Daylight Time)
toDateString()Wed Jun 01 2005Wed Jun 01 2005 00:00:00 GMT+0100 (GMT Daylight Time)
toTimeString()12:00:00 GMT+0100 (GMT Daylight Time)Invalid Date
toLocaleString()01 June 2005 12:00:00Wed Jun 01 2005 12:00:00 GMT+0100 (GMT Daylight Time)
toLocaleDateString()01 June 2005Wed Jun 01 2005 00:00:00 GMT+0100 (GMT Daylight Time)
toLocaleTimeString()12:00:00Invalid Date
toGMTString()Wed, 01 Jun 2005 11:00:00 GMTWed Jun 01 2005 12:00:00 GMT+0100 (GMT Daylight Time)
toUTCString()Wed, 01 Jun 2005 11:00:00 GMTWed Jun 01 2005 12:00:00 GMT+0100 (GMT Daylight Time)
Internet Explorer 6
toString()Wed Jun 1 12:00:00 UTC+0100 2005Wed Jun 1 12:00:00 UTC+0100 2005
toDateString()Wed Jun 1 2005Wed Jun 1 00:00:00 UTC+0100 2005
toTimeString()12:00:00 UTC+0100NaN
toLocaleString()01 June 2005 12:00:00Wed Jun 1 12:00:00 UTC+0100 2005
toLocaleDateString()01 June 2005Wed Jun 1 00:00:00 UTC+0100 2005
toLocaleTimeString()12:00:00NaN
toGMTString()Wed, 1 Jun 2005 11:00:00 UTCWed Jun 1 12:00:00 UTC+0100 2005
toUTCString()Wed, 1 Jun 2005 11:00:00 UTCWed Jun 1 12:00:00 UTC+0100 2005
Opera 9.64
toString()Wed Jun 01 2005 12:00:00 GMT+0100Wed Jun 01 2005 12:00:00 GMT+0100
toDateString()Wed, 01 Jun 2005Wed Jun 01 2005 00:00:00 GMT+0100
toTimeString()12:00:00 GMT+0100Sat Oct 17 2009 12:00:00 GMT+0100
toLocaleString()01/06/2005 12:00:00Thu Jan 06 2005 12:00:00 GMT+0000
toLocaleDateString()01/06/2005Thu Jan 06 2005 00:00:00 GMT+0000
toLocaleTimeString()12:00:00Sat Oct 17 2009 12:00:00 GMT+0100
toGMTString()Wed, 01 Jun 2005 11:00:00 GMTWed Jun 01 2005 12:00:00 GMT+0100
toUTCString()Wed, 01 Jun 2005 11:00:00 GMTWed Jun 01 2005 12:00:00 GMT+0100

 


 

 

 

Inherits from StrValue through NumValue, an abstract object not intended to be instanced.

Constructor parameters:

aValueInitial value for the date & time (as a Date object)
anElementID of the ancestor containing the <input>
aMinMinimum allowable value, approx default year -285616
aMaxMaximum allowable value, approx default year 287586

 

setURLValue()

Before the form is submitted, set into the field as an unambiguous URL parameter the milliseconds since midnight 1/1/1970.

Parameters:

aValueThe value for the URL parameter, default this.value.

 

TimeValue

Time input.  Takes a time as either 12hr HH:MM:SS AM/PM or 24hr HH:MM:SS or HHMM and stores it in a Date object as a time, depending on the isDay parameter, either on the day of the initial value, aValue, or on 01/01/1970, the latter ensuring that TimeValues can be used safely in calculations involving full dates.


 

 

Inherits from DTValue.

Constructor parameters:

aValueInitial value for the time (as Date object)
anElementID of the ancestor containing the <input>
isDaytrueUse same day as aValue
undefined or other valueUse 01/01/1970
aMinMinimum allowable value, approx default year -285616
aMaxMaximum allowable value, approx default year 287586

 

SelValue

SELECT input.  OPTIONS can be programmed from an array or a dictionary, which may have been created programmatically or read from a file.

Note that in this example page, the selected value doesn't change until the SELECT element loses focus, because updating is triggered by the onblur rather than the onchange event  -  this is because, unfortunately, the latter's behaviour is inconsistent across browsers.

This is an array example.  The data is specified as:

[ "AA", "BB", "CC", "DD" ]


 

 

 

This is another array example.  The data is specified as:

[ ["AA","00"], ["BB","11"], ["CC","22"], ["DD","33"] ]


 

 

 

This is another array example.  The data is specified as:

[
{text:"AA", value:0},
{text:"BB", value:10},
{text:"CC", value:20},
{text:"DD", value:30}
]


 

 

 

This is another array example.  The data is specified, along with the required conversion function parameter, as:

[ {x:"AA", y:0}, {x:"BB", y:100}, {x:"CC", y:200}, {x:"DD", y:300} ],
new Function(
"aValue",
"anOption",
"anOption.text=aValue.x; anOption.value=aValue.y;"
)


 

 

 

This is a dictionary / hashtable / object example.  The data is specified as:

{ "a":"AA", "b":"BB", "c":"CC", "d":"DD" }


 

 

 

This is another dictionary example.  The data is specified, along with the required conversion function parameter, as:

{ "a":{x:"AA",…}, "b":{x:"BB",…}, "c":{x:"CC",…}, "d":{x:"DD",…} },
new Function(
"aValue",
"anOption",
"anOption.text=aValue.x;"
)


 

 

 

This is another dictionary example.  The data is specified, along with the required conversion function parameter, as:

{
"a":{x:"AA",y:"00"},
"b":{x:"BB",y:"11"},
"c":{x:"CC",y:"22"},
"d":{x:"DD",y:"33"}
},
new Function(
"aValue",
"anOption",
"anOption.text=aValue.x; anOption.value=aValue.y;"
)


 

 

 

Inherits from StrValue.

Constructor parameters:

aValueDefault value to be selected initially
anElement ID of the ancestor containing the <select>.  The expected arrangement is similar to the others, but note the optional extras designed to cope with the possibility that a user's choice in a <select> might change the layout of the form (for example, a man does not need a Maiden Name field):
  • onchange event to update the variable  -  this is a problematic event, because browsers fire it differently, some even firing it differently depending on whether the keyboard or mouse is being used: if the keyboard, FF fires it on exiting the element if the final selection is different to that on entry, while, illogically in my opinion, FF using the mouse, IE and Opera fire it every time the user chooses a new selection, before the choice is finalised by leaving the element.  Thus it is almost impossible to program consistent behaviour across browsers.  Where a <select> changes layout, this is the only arrangement I have got to work, but behaviour is inconsistent.  Where layout is constant, onblur has the advantage of consistency.
  • onkeydown event to track if the <select> is being exited via the <TAB> key.
  • onblur to set the next element to receive focus after a layout change.

<div id="dSel0" title="[field help]">
<div id="lSel0"><label for="iSel0">Field Label</label></div>
<select id="iSel0" name="iSel0"
onchange="this.oData.setValue();"
onkeydown="logKey(this,event)"
onblur="setNext(this);"
>
[Options]
</select>
</div>

theValues Optional Array or Object containing replacements for the default HTML <option>s, which are lost if this parameter creates at least one <option>.  Replacements can be:
  • An Array of Option DOM Objects  -  these simply replace the existing HTML <option>s with no further processing.
  • An Array of Strings  -  each string becomes an Option.text and its index the corresponding Option.value.
  • An Array of an Array of Strings  -  the first element in each string array becomes Option.text, the second Option.value.
  • An Array of Objects each with text and value properties  -  these become the corresponding Option properties.
  • An Array of Objects each with properties to be mapped by the following conversion function.
  • A dictionary (Object) of Strings  -  each string (property value) becomes Option.text and key (property name) Option.value.
  • A dictionary of Objects each with text and value properties  -  these become the corresponding Option properties.
  • A dictionary of Objects each with properties to be mapped by the following conversion function.
fConvert A conversion function should be specified, otherwise an error will likely occur, where each Option is to be derived from an Object, the source fields in which are not called text and value.  Optionally, the function may also contain a statement to set the selected option, but, if so, must only set one selected option; for example:
if(<unique condition>) anOption.selected = 'selected';

 

getDefault()

Returns the default option's value.

Parameters:  None

 

selDefault()

Selects the default option.

Parameters:  None

 

setDefault()

Sets the default option without changing the selection.

Parameters:

aValue Value of an existing option which is to become the new default option.

 

loadOptions()

Sets new options into the Select element as described in the constructor parameters.

Parameters:

theValues Replacement options as described in the constructor parameters.
fConvert Conversion function as described in the constructor parameters.

 

debounceSetValue()

In those browsers that fire an onchange event for every change in selection, even when the user fast scrolls through the options using the arrow keys, provides timeout debouncing to ignore onchange events until scrolling ceases and a choice made, whereupon setValue() is called in the normal way.

Parameters:

myVarName String representing the global variable name of this SelValue.  For example, if it was defined as …
var aVarName = new SelValue( … )
… then the call from the associated <select> element would be (note the parameter is quoted) …
onchange="this.oData.debounceSetValue('aVarName');"
(reserved) Do not specify this parameter.

 

submitURLPars()

Submit a form, optionally to a different URL or target.  Before submission, the form is scanned …

Parameters:

aFormEither the form element to be submitted, or its id.
aUrlThe URL for the submission, default is the page URL.
aTargetThe target for the URL submission, default is _self.

 

setURLPars()

Set given URL parameters into the designated elements in the form, updating the related data objects 'behind' the elements.  This would normally be called from the body.onLoad() event, once form initialisation is complete.

Parameters:

theParsThe parameters to be set, default are those from the page URL.

 

logKey()

Log keystrokes occurring on an element, and appropriately set element property, lastKey.  This property is useful for trying to create programmatically a sensible tab order by focusing the correct next element when a user <Tab>s or <Shift-Tab>s out of a Select element where his choice might have changed the visibility of other elements in the form.

KeystrokeanElement.lastKey
<Tab>"N"
<Shift-Tab>"P"
Other""

Parameters:

anElementThe element whose manner of leaving it is required to log.
anEventThe event corresponding to the keystroke on the element.

 

setNext()

In browsers other than Internet Explorer, if the element designated has lastKey == "N" and a property next, focus and select that next element.

Parameters:

anElementThe element about to be exited.

 

Submit
 
 
Tests the Submit functionality.  Calls submitURLPars() to set URL-compatible values into those fields requiring them, performs a GET, and calls setURLPars() to set the URL parameters back into the form.  

Form Styling with CSS

Form styling is largely a matter of choice but here are some tips:

Appendix  -  Required Code From Other Scripts

The following functions, from other scripts on my site, are required for "Forms.js" to run (all four for this version, first two only for earlier versions), but were not included or documented in earlier versions of this page.  Apologies for any inconvenience caused by this oversight.  They are:
// Add or remove a class to or from an element
function setClass( anElement, aClass, set )
{
var theElem = typeof anElement == "string" ? document.getElementById( anElement ) : anElement;
var classStr = null;
try
{
var regExp = new RegExp( "\\b" + aClass + "\\b", "g" );
classStr = theElem.className.replace(regExp,"");
if( set )
classStr += " " + aClass;
theElem.className = classStr;
}
catch(e){}
return classStr
}
// Show or hide an element
function setShowHide( anElement, show )
{
return setClass( anElement, "invis", !show );
}
// Walk a DOM subtree, looking for descendants of certain types
function TreeWalk( aNode, theTypes )
{
var node = typeof aNode == "string" ? document.getElementById(aNode) : aNode;
var types = theTypes instanceof Array ? theTypes : [theTypes];
var result = [];
var nodes;
// Note: for( var x in y ) on an array bugs in some browsers,
// especially if Array.prototype has been altered
if( node )
{
if( node.tagName )
for( var i = 0, I = types.length; i < I; i++ )
if( node.tagName.match(new RegExp(types[i],"i") ) )
result.push( node );
nodes = node.childNodes;
for( var i = 0, I = nodes.length; i < I; i++ )
result = result.concat( TreeWalk(nodes[i], types) );
}
return result;
}
// Extend String to have trim function
String.prototype.trim = strTrim;
function strTrim()
{
return this.replace(/(^\s+)|(\s+$)/g, "");
}

Updated Description
13/06/2010 v1.1  -  Safer Array for() loops if the Array.prototype is altered.
05/12/2009 Updated introduction with an IE bug concerning <select>s, and interleaved this page's associated HTML & JS coding to avoid it.  Altered documentation and coding to specify calls from elements using this.oData.setValue().  Added trim parameter.  Added checkbox and radio-button functionality.  Generalised setURLValue() functionality.  Changed update(), submitURLPars(), setURLPars(), and associated code to use read only rather than disabled non-active inputs, and removed requirement for a form element name to be the same as its id.  Added onchange debouncing for SelValue.  Changed setNext() to allow an id for next element.  Added use of TreeWalk() to find elements of particular types.  Added CSS section.  Added missing functions from other scripts to make standalone.  Moved from test area to website proper.
27/10/2009 Documented expected field layout.  Further documented Internet Explorer layour bug.  Fixed problem with dates and times in URL parameters being potentially ambiguous in some combinations of browser and locale.  Improved floating-point URL parameters.
23/10/2009 Originally created