|
On occasions it is handy to be able to let a user add or remove
items from a list on a form. This is fairly easy to do as the options
within a <SELECT> list can be manipulated
through simple JavaScript.
Consider this table wrapped form definition.
<table cellpadding="10" bgcolor="#ffeecc">
<form method="GET" action="#">
<tr valign="top">
<td rowspan="2">Options</td>
<td rowspan="2">
<select name="list" size="10" style="width:150px">
</select>
</td>
<td>
<input type="text" name="newtext" size="25" value=""/>
<input type="button" name="addtext" value="Add" onclick="addText(this.form)"/>
</td>
</tr>
<tr valign="bottom">
<td>
<input type="button" name="delsel" value="Remove" onclick="delSelItem(this.form)"/>
</td>
</tr>
</form>
</table>
Which renders as below.
Typing into the text box on the right and pressing the Add button
will add the text into the select list options array, causing an extra entry
to appear in the select list box. If the entered value is already in
the select list options then it will not be added again. Selecting an
option and pressing the Remove button will remove the
selected option from the list.
So how is this done ?
Adding an option
The onclick event function specified for the Add button
is called when the button is pressed. The argument, this.form,
passes the form object associated with the button to the function. The
function then fetches the value of the text box from the form, checks to
see if the value is already in the options list and, if not present, adds
it in. The function is shown below.
function addText(f){
var str = f.elements['newtext'].value;
var opts = f.elements['list'].options;
if ( ! inOpts(str,opts) ) {
opts[opts.length] = new Option(str);
}
f.elements['newtext'].value = "";
}
Both the text box and select list are referenced by name in the form's
elements array to retrieve their properties, the text value for the
text box and the array of options for the select list. Using the associative
array access, effectively indexing into a hash table, is the easy way
to get hold of a form's input objects, but requires that those elements
to be accessed in this way have unique name attribute values.
To check whether the text from the text box is already in the options list,
the addtext function makes use of another function, inOpts,
which returns true if the given text is already an option, or
false if not. The function is shown below.
function inOpts(str,arr){
for( var i = 0; i < arr.length; i++){
if ( arr[i].text == str ) return true;
}
return false;
}
As arguments, the inOpts function expects the string to check
in the options array, and the options array itself. A simple loop over
the contents of the array is performed, checking the string against
the text property of each element for equality.
If the text from the text box does not appear in the array, it must be
added. An object to represent the OPTION form element must be
created via a call to its constructor function, and then added to the array.
Constructor functions are special in as much as they call into being a object
of a given type, with properties set to default values appropriate to the
type of the object. In this case the object type is Option. A new
object is created using new JavaScript keyword. Some object
constructors can take arguments to set properties immediately upon creation
of the object. Such is the case with the Object constructor. In
this example the text to display and the value of the option is set
simultaneously from the same value passed as the sole argument to the
constructor.
Once the object is created it is added to the options array at the end
using a simple but effective method. The first object in a JavaScript
array is element 0, so the value of the length property will always be
1 more than the highest possible index value (an array with 5 elements
element will have a length property value of 5, but a maximum index
location of 4). So, to add another element at the end of an array it
is always possible to assign a value to the index location of the
current length of the array.
By adding a new Option object to the options array, the
select list automatically redisplays itself with the updated list of
options, adding scrollbars as appropriate. As the width of the element
has been set to a specific value, any option text that is wider than
the element will not be displayed fully.
In this example, once the text box value has been checked and possibly
added to the select list, the text box value is set to the empty string
in order to provide visual feedback to the user that the operation
has completed.
Okay, that deals with adding to the list of options, so how about the
deletion of options, how is that done ?
Deleting an option
By pressing the Remove button it's onclick event function,
delSelItem, is called. The argument value this.form passes
the form object associated with the button to the function, as with the
addtext function explained above. The object representing the select
list element is fetched from the form elements array by referencing
it's name.
As all of the available options for a select list are held in it's
options array, the currently selected option must be in that array.
The value of the selectedIndex property holds the index location
of the currently selected option (the one that is highlighted) in the array.
However, if no option has been selected, the value of the selectedIndex
property cannot lie within the array bounds (0 to length - 1), and so a value
of -1 is used to indicate this.
Once the selected option's index location has been found, a simple check to
ensure it is greater than -1 is made to see if an option was indeed selected
before the button was pressed. If no option was selected the function returns
early. If an option was selected it must be removed from the array. There is
a very simple way to do this with the select list options array; assign a
null value to the index location. This causes the select list to
remove the option from the array for us, and redisplay itself appropriately.
The event function is shown below.
function delSelItem(f){
var selel = f.elements['list'];
var idx = selel.selectedIndex;
if ( idx < 0 )return;
selel.options[idx] = null;
}
Multiple choice SELECT lists
The functions above work well for single choice select lists, but what
about those with the multiple attribute set ? These types of
select lists allow for more than one option to be selected at once. This
means that the selectedIndex property cannot be used to determine
which options must be removed from the options array, as only a
single value can be held. the addition of options to the select list does
not have to change as the multiple attribute only affects
selected options.
Assuming that there are multiple options selected, to be able to delete them
this time it is still possible to use the same code, but just press the
Remove button multiple times. Each time the button is pressed
the first option in the list that is selected will be removed. This is not
really satisfactory. Instead it is more desirable to remove all the selected
options at the same time.
A simple modification to the delSelItem function can do this. First,
let us define a new form, this time with the multiple attribute
specified in the <select> element. The onclick event
function for the Remove button in this form points to a modified
version of the delSelItem function, called delAllItems.
The new function checks the value of the multiple property, which
is set to true if the select list has the multiple
attribute set. If the value is not true, indicating that only a
single option can be selected at once, the same code as before is executed
to remove the selected option from the select list. If the value is true
new code executes, which starts by getting and holding a reference to the select
list options array to use later on. Then it loops around the elements
in the array checking the value of the selected property for
each one. This is the equivalent of the SELECTED attribute
in the OPTION element. When the value is true it means
that the associated option has been selected and therefore needs to be
removed. Removal is performed in the same way as before, namely assigning
a null value to the array index location.
The new function is shown below.
function delAllItems(f){
var selel = f.elements['list'];
if ( selel.multiple == true ){
var opts = selel.options;
for ( var i = 0; i < opts.length; ){
if( opts[i].selected == true ){
opts[i] = null;
} else {
i++;
}
}
} else {
var idx=selel.selectedIndex;
if( idx < 0 ) return;
selel.options[idx] = null;
}
}
Notice how the for loop is controlled. In normal usage a for
loop is specified with an initialiser statement that sets up a loop variable,
a loop variable test and a post body statement that usually alters the value
of the loop variable. In this case there is no post body statement (the expected
i++ to point to the next array location is missing), only the
initialiser (i = 0) and the loop test (i < opts.length)
are present. This is because the loop variable must not be incremented if an
option has been removed, as all of the elements following it have actually
moved down by one location in the array, and so it is necessary to check the same
location index value again. If the next location was checked an option would be
missed because of the shifting down. The loop variable is only incremented when
an option is not selected.
|