reptile7's JavaScript blog
Tuesday, September 19, 2017
 
The Option Optic
Blog Entry #380

In casting about for a more efficient way to code the selection list data of the Money Conversion Script I came across a related Example from JavaScript 1.1-1.3 that is itself worthy of a brief post.

Two exceptional aspects of classical (pre-DOM) JavaScript are highlighted by the Example:
(1) A client-side Option object can be created via a new Option( ) constructor operation (classical JavaScript featured two constructible objects, Image being the other).
(2) Alone* among the various client-side array** properties (document.forms, window.frames, etc.), the Select.options array is writable.
We will apply these aspects to the Unit and Unit2 selection lists in the following entry.

*Nope, we can't create an HTML <img> element with
var myImage = new Image( );
myImage.src = "some_image.png";
document.images[0] = myImage;

and we can't create an HTML <a> element with
document.links[0] = "Link text".link(linkURL);,
or at least I can't on my computer.

**These "arrays" (Netscape's term) are not core Array objects in that
(a) excepting length you can't use the Array object's properties and methods with them and
(b) their childObject members can be referenced associatively (parentObject.childObjects["identifierString"]) as well as ordinally (parentObject.childObjects[indexNumber]).

Starting structure

The Example's HTML codes a select-one selection list and a select-multiple selection list that have no option children.

<h3>Select Option( ) constructor</h3>
<form>
<select name="selectTest"></select><p>
<input type="button" value="Populate Select List" onclick="populate(this.form);">
<p>
</form>
<hr>
<h3>Select-Multiple Option( ) constructor</h3>
<form>
<select name="selectTest" multiple></select><p>
<input type="button" value="Populate Select List" onclick="populate(this.form);">
</form>


Each selection list sits in a separate and unnamed form, is itself named selectTest, and is paired with a push button. Clicking that button calls a populate( ) function that is supposed to stock the selection list with a set of four options - and will, once we get rid of an offending line of code.

Build it and they will opt

The populate( ) call passes to populate( ) a this.form reference to the parent form, which is given an inForm identifier.

function populate(inForm) { ... }

The populate( ) body begins by defining a colorArray Array of color keyword strings; none of populate( )'s subsequent operations calls on colorArray, however.

colorArray = new Array("Red", "Blue", "Yellow", "Green");

Next, populate( ) constructs four Option objects for the four colorArray colors.

var option0 = new Option("Red", "color_red");
var option1 = new Option("Blue", "color_blue");
var option2 = new Option("Yellow", "color_yellow");
var option3 = new Option("Green", "color_green");


The first new Option( ) argument specifies the Option text; the second new Option( ) argument specifies the Option value. The Option objects are given the identifiers option0, option1, option2, and option3, respectively.

The option0-option3 objects are iteratively loaded into the empty inForm.selectTest.options array, a process that produces corresponding Red/Blue/Yellow/Green HTML option elements and appends them to the selectTest select element; the options[0] Red option is selected once the options are in place.

for (var i = 0; i < 4; i++) {
    eval("inForm.selectTest.options[i] = option" + i);
    if (i == 0) {
        inForm.selectTest.options[i].selected = true; } }


The populate( ) action concludes with a

history.go(0);

command that was required to render the option HTML with Netscape 3.x but clears the options with modern browsers and must now be removed for the Example to work as it should.

eval( ) exit

The eval( ) command that coordinates the selectTest optionization does what it is supposed to do but nonetheless isn't very ... elegant, shall we say - here are some more satisfying code possibilities:

(1) We can organize the new Option objects as a bona fide Array object and then write them as Array elements to the inForm.selectTest.options array.

var colorOptions = new Array( );
colorOptions[0] = new Option("Red", "color_red", false, true);
colorOptions[1] = new Option("Blue", "color_blue");
colorOptions[2] = new Option("Yellow", "color_yellow");
colorOptions[3] = new Option("Green", "color_green");
for (var i = 0; i < colorOptions.length; i++)
    inForm.selectTest.options[i] = colorOptions[i];


The new Option( ) constructor can take a third argument specifying the Option's defaultSelected status and a fourth argument specifying the Option's selected status. If we create the colorOptions discretely, then we can select the Red Option at the constructor stage, in which case a separate selected = true assignment is unnecessary.

(2) As shown earlier, the new Option texts are colorArrayed in the original code; if we similarly pre-Array the new Option values, then we can iteratively create the Options and write them as themselves to the inForm.selectTest.options array.

var colorOptionTexts = new Array("Red", "Blue", "Yellow", "Green");
var colorOptionValues = new Array("color_red", "color_blue", "color_yellow", "color_green");
for (var i = 0; i < colorOptionTexts.length; i++)
    inForm.selectTest.options[i] = new Option(colorOptionTexts[i], colorOptionValues[i]);
inForm.selectTest.options[0].selected = true;


The corresponding createElement( )/appendChild( ) route would require a few more lines of code:

for (var i = 0; i < colorOptionTexts.length; i++) {
    var colorOption = document.createElement("option");
    colorOption.text = colorOptionTexts[i];
    colorOption.value = colorOptionValues[i];
    inForm.selectTest.appendChild(colorOption); }


(3) We can pre-organize the new Option texts and values as a two-dimensional Array and then proceed à la (2) above.

var colorOptionData = new Array( );
colorOptionData[0] = ["Red", "color_red"];
colorOptionData[1] = ["Blue", "color_blue"];
colorOptionData[2] = ["Yellow", "color_yellow"];
colorOptionData[3] = ["Green", "color_green"];
for (var i = 0; i < colorOptionData.length; i++)
    inForm.selectTest.options[i] = new Option(colorOptionData[i][0], colorOptionData[i][1]);
inForm.selectTest.options[0].selected = true;


Mozilla has been warning us for a while that eval( ) is a dangerous function (specifically, eval( ) can enable a cross-site scripting attack depending on the UI) and is also generally slower than the alternatives; these considerations are unimportant vis-à-vis the Example, but given that loops and arrays are meant for each other, we really ought to be pressing Mr. Array into service here, yes?

Demo

I deploy the two-dimensional colorOptionData Array in the following demo; check the source of the current page for the full coding.

The bordered div below holds two empty selection lists. Click the buttons to populate the lists with options on the fly.

Select-One Option( ) Constructor




Select-Multiple Option( ) Constructor




Comments: Post a Comment

<< Home

Powered by Blogger

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