Looping Through Forms: Best Practice

D

David

Hello.

I am looking for advice on what is "best practice" regarding looping
through a form to check its checkboxes and associated data fields.

Here is what I am trying to do (Here is the page I am working on:
http://www3.telus.net/thothworks/LinLeastSqPoly4.html).
I provide a form for a user to enter up to twenty (M = 20) data pairs.
The user need not enter data for all twenty pairs, but
the user must indicate that data is present by checking the checkbox
beside each valid pair.

If a checkbox is checked, two things happen:
i) a counter, numRows, is incremented to keep track of the total
number of valid pairs entered.
ii) the associated two data values (being x and y) are checked to
ensure that they are valid numbers and, if so, are entered in an array
for use later.

Previously, my forms were small, and I was able to examine each
checkbox of a form individually:
"if (checkbox1.checked) do action A;
if (checkbox2.checked) do action B;
if (checkbox3.checked) do action C;
etc."

However, now that my form has twenty checkboxes, and may get bigger, I
would like to learn how to accomplish this task with a concise loop.
Following is my first attempt and it seems to work. If anyone can
suggest improvements for this code, or even a better/different way to
achieve the goal, that would be appreciated.

=================

var M = 20; //Global variable, MAX number of rows of A matrix.
var N = 2; //Global variable, number of columns of A matrix.

function llsqpy4Solve(form){

var numRows = 0; // Total number of valid data pairs input
var checkIndex = 0; // Form index for checkboxes
var xIndex = 1; // Form index for x data field
var yIndex = 2; // Form index for y data field
var tempx, tempy; //Dummy variables

var A = new Array(M);

for (var i = 0; i < M; i++)
A = new Array(N);

for (var i = 0; i < M; i++) {//Examine the 20 data fields
if (document.forms[0].elements[checkIndex].checked){
tempx = parseFloat(document.forms[0].elements[xIndex].value);
tempy = parseFloat(document.forms[0].elements[yIndex].value);
if (!isNaN(tempx) && !isNaN(tempy)){ //Both fields contain valid
numbers
A[numRows][0] = tempx;
A[numRows][1] = tempy;
numRows++;
}//End if !isNaN
} // End if checkbox checked
checkIndex += 3;
xIndex += 3;
yIndex += 3;
}// End for i loop

.. . .

}// End of function llsqpy4Solve
 
R

Richard Cornford

var M = 20; //Global variable, MAX number of rows of A matrix.
var N = 2; //Global variable, number of columns of A matrix.

I don't see any reason for N being a global variable (or a variable at
all) as you have hard coded the number of fields being read by the
function as two so you may as well replace all references to N within
the function with the number 2.
function llsqpy4Solve(form){

"form" may not be the best name to give to this parameter (maybe oForm)
as it is usually best to avoid identifiers that correspond with DOM
object properties such as the "form" property of form elements (like
the - this.form - that is passed to this function. However, this
parameter identifier is invisible outside of this function so using this
name will not actually case problems.
var numRows = 0; // Total number of valid data pairs input
var checkIndex = 0; // Form index for checkboxes
var xIndex = 1; // Form index for x data field
var yIndex = 2; // Form index for y data field
var tempx, tempy; //Dummy variables

var A = new Array(M);

There is not need to provide a dimension for the Array constructor,
doing so is harmless but not doing so may have advantages. You might
consider using Array literal syntax when creating arrays, - var A =
[]; - as it is slightly less bytes to download.
for (var i = 0; i < M; i++)
A = new Array(N);


Adding M two element arrays to A in this separate loop can be avoided. I
will explain later.
for (var i = 0; i < M; i++) {//Examine the 20 data fields
if (document.forms[0].elements[checkIndex].checked){

You have passed this function a reference to the form object (the "form"
parameter) so there is no need to re-resolve the reference to the form
each time you access one of its elements (60 times). You could use -
form.elements[checkIndex].checked - but I would be inclined to avoid
re-resolving the reference to the elements collection as well by
creating another local variable that referred to that and using it to
reference the elements:-

var oFrmElements = fome.elements;
....
if(oFrmElements[checkIndex].checked){
....
tempx = parseFloat(document.forms[0].elements[xIndex].value);

The optimum string to number type converting method is the unary plus
operator:-

tempx = (+oFrmElements[xIndex].value);

- it is about 4 times faster than parseFloat and still produces NaN
results if the string cannot be converted into a number.

You could still be indexing the elements collection by name, using a
string that represents the start of the name and concatenating the for
loop counter:-

tempx = (+oFrmElements['x'+i].value);

- removing the need to have checkIndex, xIndex and yIndex as local
variables and allowing additional elements to be added to the form
before the required fields without requiring the re-writing of the
function. Though the for loop counter would have to start at 1 and count
to <= M.
tempy = parseFloat(document.forms[0].elements[yIndex].value);
if (!isNaN(tempx) && !isNaN(tempy)){ //Both fields contain
//valid numbers
A[numRows][0] = tempx;
A[numRows][1] = tempy;
numRows++;

You can add the two element arrays as elements of A at this point. If A
had not been pre-dimensioned to M elements you could add elements at
A.length and avoid the need for a numRows local variable, later reading
A.length when you needed to know how many items had been assigned to the
array:-

A[A.length] = [tempx, tempy];
}//End if !isNaN
} // End if checkbox checked
checkIndex += 3;
xIndex += 3;
yIndex += 3;
}// End for i loop

. . .

}// End of function llsqpy4Solve

So:-

var M = 20; //Global variable, MAX number of rows of A matrix.

function llsqpy4Solve(form){

var oFrmElements = form.elements;
var tempx, tempy, A = [];

for (var i = 1; i <= M; i++) {//Examine the 20 data fields
if (oFrmElements['p'+i].checked){
tempx = (+oFrmElements['x'+i].value);
tempy = (+oFrmElements['y'+i].value);
if (!isNaN(tempx) && !isNaN(tempy)){ //Both fields contain
// valid numbers
A[A.length] = [tempx, tempy];
}//End if !isNaN
} // End if checkbox checked
}// End for i loop

....

}// End of function llsqpy4Solve

Richard.
 
D

Dr John Stockton

JRS: In article <[email protected]>, seen
in news:comp.lang.javascript said:
Thank-you, Richard.

You have given me a lot of good advice.

One suggestion that you made completely surprised me:

******
The optimum string to number type converting method is the unary plus
operator:-

tempx = (+oFrmElements[xIndex].value);

- it is about 4 times faster than parseFloat and still produces NaN
results if the string cannot be converted into a number.


Nevertheless, one must be careful. I find that
parseFloat('0x1') differs from +'0x1'
parseFloat('') differs from +''


Richard has, I think, given the fastest and best way of converting a
string with optional sign then all digits.

But for converting input from the user via a text control, unary + will
be fastest, but the best should be something with checks, like :

function Read(S, Q) {
if (/^[-+]?\d+$/.test(S)) return +S
alert(Q) ; return NaN }

tempx = Read(oFrmElements[xIndex].value)
 
L

Lasse Reichstein Nielsen

I have about three Javascript books at home, but I have never come
across this feature.
Is this method new?

Not all new.
Does it adhere to the ECMA standard? (I avoid functions that only
work on particular browsers.)

It does. ECMA says:
---
11.4.6 Unary + Operator
The unary + operator converts its operand to Number type.

The production UnaryExpression : + UnaryExpression is evaluated as follows:

1. Evaluate UnaryExpression.
2. Call GetValue(Result(1)).
3. Call ToNumber(Result(2)).
4. Return Result(3).
----

It won't work in all browsers (e.g., Netscape 2 and 3), but it seems to
work in modern browsers (Netscape 4, Opera 4+). I can't check old IE's,
but I would guess IE 3 could have a problem.

/L
 
D

David

Thanks for the suggestion, John.
Your suggestion raised a couple additional questions.

Wouldn't the unary operator check for invalid input anyhow? Otherwise,
why does it have the ability to return NaN?

Same question for parseFloat: if I use parseFloat, should I also use
the check function you proposed? If not, maybe I am better off using
parseFloat (Basically I am wondering, if parseFloat does NOT require
the check function you propose, is it better to (i) use the unary
operator with your check function, or (ii) simply use parseFloat?).


David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top