PDA

View Full Version : can you place an if statement in a for conditional?



sniperman
01-12-2010, 09:11 AM
Hi, my latest attempt with JavaScript is to take a couple of variables in the innerHTML of page spans to tally a total value.

I use an event handler in the body


<select id="retreat1" onchange="tallyTotals(this)">
<option value="selected"></option>
<option value="value1">1</option>
<option value="value2">2</option>
</select>


to call a script similar to this... so far.


function tallyTotals() {
for(var i=1;if(document.getElementById('retreat'+[i]));i++)
{
alert("Hello");
}
}

There are several select tags in the form with values I would like tallied and shown in an innerHTML. I know there are probably better methods I will be shown by this article, and by all means I would like to learn.

But my instincts tell me there is a syntactical method which will allow any argument inside the parameters for the for loop.

djr33
01-12-2010, 09:25 AM
I don't see any logical reason that would not work (with one important change*), but that is really a bad idea:
that will continuously generate "hello" (repeatedly), until something changes-- if you are repeating the alert, then nothing can change-- it will get stuck forever.

A for loop is somewhat useless, in the sense that it is the same as while:

for (statement 1; statement 2; statement 3;) {
...
}

//same as

statement 2;
while (statement 1) {
...
statement 3;
}
So in this sense there is no reason you could not just use a while loop instead-- a for loop is just a specific complex version of a while loop, and that should work also.


*The only real problem is that you are basically using "if" twice:
Again, let's simplify the problem to a while loop:
while (condition)
Means "continue doing this if (condition) is true"
while (if(condition))
Means "continue doing this if (if(condition)) is true"

Because of this, you do not need the "if" at all-- just what you would put INSIDE the if, now inside the for loop:
for(var i=1;document.getElementById('retreat'+[i]);i++)


technically, I suppose that may not harm anything, but it is a weird way to code--
the inside if will return true or false, then:
if (true) => true
if (false) => false
So, really, there is no harm, but it is certainly not needed.



Anyway, as for the idea overall, the best way to approach it is probably entirely different: use events or perhaps a silent loop that looks for this condition and acts on it, instead of using the sort of loop above.
1. Try something like body.onclick = dosomething();
or
2. while() { ///empty condition: always true, or you could use while(true)
if (condition) { dosomething(); }
}
That would loop forever waiting until 'condition' is true, and then dosomething().

jscheuer1
01-12-2010, 10:55 AM
function tallyTotals(){
for(var i = 1; document.getElementById('retreat' + i); ++i){
alert("Hello");
}
}

I don't know about the if. The above will work. It will continue to loop until there are no more document.getElementById('retreat' + i) elements'.

For loops are better than while loops if there's a lot of looping going on, more efficient.

Generally though, aside from the syntax, it makes no difference. However, unless you know for an absolute fact that your code will never have many loops, for is better.

sniperman
01-12-2010, 12:21 PM
First off, thank you very much for both your contributions. I knew it was a weird way to code but I wanted to eliminate some redundant looping and reduce the loop rate to the actual elements, which would in turn build an array.

The original function went something like this.


var tagsarray = new Array();

function defineTotal() {

for(var i=1;document.getElementById('retreat' + i);i++) // count all instances of the select form field
{
if (document.getElementById('retreat'+[i])) {
tagsarray.push(document.getElementById('retreat'+[i])) // list all instances in an array
}
}
alert(tagsarray[0].options[tagsarray[0].selectedIndex].value);
}


I suppose the conventional way to do this is written below and I eventually used this.


var tagsarray = new Array();

function defineTotal() {
tag = document.getElementsByTagName('select');

for(var i=0;i<tag.length;i++) // count all instances of the select form field
{
if (document.getElementById('retreat'+[i])) {
tagsarray.push(document.getElementById('retreat'+[i])) // list all instances in an array
}
}
alert(tagsarray[0].options[tagsarray[0].selectedIndex].value;
}

But, after I came back to this forum and read the posts, I removed the if statement from the for loop's statements (otherwise I guess I would have created a subroutine). And I reverted from the conventional way to my original way because it removed the need to cycle through with getElementsByTagName().

The point of the exercise, in my mind, was to loop only through direct instances of the tag controlled by an incremental id (i.e. retreat1, retreat2).

I know it's clunky, and I am now having trouble looking for a good way to obtain the value of the selected index and to use this value in some eCommerce kind of fashion to calculate a total.

jscheuer1
01-12-2010, 04:01 PM
There are various ways to make something like this more efficient. I'm not really clear as to using the getElementsByTagName() being more efficient than sequential calls to getElementById(). It depends on the document. The byTagName method can target elements that are children of another element, thus narrowing down the scope of the node list, while byId cannot. A single use of byId is faster, but not multiple ones. I'm not clear on how many it would take to be slower than byTagName, probably not too many. With byTagName your code could more easily apply to multiple forms on one page, giving results for each individual form. There are also document.getElementsByName() and the form's elements collection, either of which can be a better choice, depending upon the situation.

Getting back to the for loop, these are most efficient when as many variables as possible are defined ahead of time, and those that cannot be are at least declared. Also, incrementing/decrementing - as long as it's still logically valid - is more efficient as what they call pre, as opposed to post. With pre, the two plus or minus signs come before the variable, with post, after. The meaning is slightly different, but often interchangeable, or adaptable. With pre the variable's value changes in the instant in which it is done, with post it doesn't change until the next instance of the variable is accessed. Because of this post must remember, pre doesn't and so is more efficient.

Here are these principles used in something like I think you are doing:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
function defineTotal(){
var tot = 0, i = 1, el;
for(i; (el = document.getElementById('retreat' + i)); ++i){ // loop all sequential instances of select's with an id of retreat#
tot += +el.value; // add their value to tot
}
alert(tot);
return false;
}
onload = function(){
document.forms.totform.onsubmit = defineTotal;
}
</script>
</head>
<body>
<form name="totform" action="#">
<div>
<select id="retreat1">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<select id="retreat2">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
<input type="submit" value="Total">
</div>
</form>
</body>
</html>

I chose byId because with just two lookups it's probably faster.

One big problem here is that it expects a lot from the markup. If things change, it may no longer be efficient and/or even work. Some markup dependency is inevitable. It should be kept to a minimum though.

sniperman
01-13-2010, 04:12 AM
The initial build I am attempting to code will have an internal panel to input dynamic variables for the form, kind of a pseudo CMS before I decide whether it is worthwhile to store the data on a server database and use JavaScript DOM/AJAX to retrieve them.

Currently this is out of my immediate scope. And thanks for highlighting markup dependency. I am seeking to make the form as automated as possible with only placeholder and IDs for variables that will change the markup. So markup dependency will cause me to rethink the approach because the html on the page will not be static.

In regards to the code, I spent 10 hours to engineer a solution. I don't mean to step on any toes but by using the second statement in your code


for(i; (el = document.getElementById('retreat' + i)); ++i)

doesn't that mean the statement is assigning whatever 'retreat'+i is, and if that is the case, the for loop will always return true because even if it returns NaN or empty it is being assigned to el? Whereas with my version I only want to check whether the ID instance exists, and if so, perform the desired functions.

This is my functional code for now... although the problem to handle now is that each for loop appends new instances of the same objects into the tagsarray Array. I am thinking I should place the array inside the for loop so it always overwrites the information.

And of course, I now have to work on a new function to minus the value once the option is deselected... coding!!


var tagsarray = new Array();
TotalPrice = 0; // the default value inside the Total Price box

function tallyTotals(fld) {
for(var i=0;document.getElementById('retreat'+i);i++) // count all instances of the select form field if its ID exists,
{
if (document.getElementById('retreat'+[i]))
{
tagsarray.push(document.getElementById('retreat'+[i])) // list all instances in an array
}
}
for (var i=0;i<tagsarray.length;i++)
{

if(document.getElementById(fld.name).name==tagsarray[i].name) // if the name is equal to the name in tagsarray
{
var retreat_price = document.getElementById('price'+[i]).innerHTML; // retrieves the associated price from span innerHTML and stores in a variable
retreat_price = retreat_price*tagsarray[i].options[tagsarray[i].selectedIndex].innerHTML; // returns the innerHTML value of the option chosen to retreat_price variable
TotalPrice+=retreat_price; // stores new retreat price into the total
document.getElementById('total').value = TotalPrice; // also returns this total to the page
}
}


}

jscheuer1
01-13-2010, 06:56 AM
As for:


for(i; (el = document.getElementById('retreat' + i)); ++i)

Notice the red parentheses. They mean evaluate this expression first. The fact of, say:


el = false;

really doesn't have any boolean value anyway, at least not due to the = sign which is an assignment operator, not a comparison operator. That line's value is the value of the result of the assignment, the value of el, false in this case.

You can test all that by putting an alert inside the loop for my code, it will fire only twice.

On to your code, and I will have a closer look at it later. The array, if required at all, could be defined within the function. So could possibly the total. It is more efficient to use the literal array syntax:


function tallyTotals(fld) {
var tagsarray = [];
TotalPrice = 0; // the default value inside the Total Price box
for(var i=0;document.getElementById('retrea . . .

Now, I've not really looked this over in detail yet. But by defining the array in the function, it will be reset each time the function runs, so will not accumulate duplicate entries, though it will not be available outside the scope of the function unless something is done to make it so, though that may not be required. Setting the total inside the function has a similar effect, and may eliminate the need for any subtraction. Each time the function is run it will only include those values that are selected, skipping any that have become deselected.

sniperman
01-14-2010, 02:23 AM
Is it true that all conditionals inside a for loop have to return boolean, i.e. TRUE or FALSE.

From what I can see

{

i exists so it's true
(el = document.getElementById('retreat' + i)) exists and returns true to el
++i is true because i exists and can be added to

}

so even if you ran a subroutine with calls to other functions and set this subroutine as a condition of a for loop, as so


for (condition1; if (new conditions) {runexternalfunction(eval)};++i;)

In theory, that should work. Although I don't know how practical that would be.

jscheuer1
01-14-2010, 10:39 AM
Only thing for that would be to try it out. However:



if (new conditions) {runexternalfunction(eval)}

might have the return value of:


runexternalfunction(eval)

not of:


(new conditions)

If (new conditions) were false, there could very well be an error, as perhaps at that point nothing to evaluate on a boolean basis would be presented to the script parser. Even if it all worked in theory, the syntax might need to be beefed up a bit.

sniperman
01-14-2010, 03:50 PM
I might keep that one in my back pocket and run some tests at a later date. It would be interesting to see a for loop with embedded functions run a form validator sequence for example, or to be an initALL() for DOM like Firebug for Firefox.

A for loop as a master control for subroutines sounds cool. Would have to be beefed up a bit though!

Thanks for all the input.

jscheuer1
01-15-2010, 06:02 AM
One thing I forgot to mention is that only the middle statement in the for line needs to return a truthy boolean value for the loop to continue:


for( var i = 0; whatever; whatever )

In the above, i = 0 is false. If i starts out as -1 and is incremented by steps of 1 in the third part of the line, it will be zero there at the first increment, once again false. The loop continues however, as long as the middle part evaluates as truthy.

There is no intrinsic requirement that any part other than the middle be boolean at all, though the first and last parts, at least in my experience, are always literal numbers (though I suppose they could be Number objects, but that would be less efficient), so will always have a boolean value of true, unless 0. BTW, a Number object is always true, even if 0.