Monday, 9 October 2017

javascript - Form iteration function not working properly

itemprop="text">


My function is supposed to
iterate all forms in the document, and bind an onclick function to each 'calculate'
element int he form. The problem is, the function that executes on any of the click
events executes in the context of the the last i in the
loop.



Here is the JavaScript that I'm
using:



window.onload = function(){

calculateSavings();
}

function
calculateSavings(){

for (i = 0; i < document.forms.length; i++)
{
var e = document.forms[i];
e.calculate.onclick = function() {

var hours = e.hours.value;
var rate = e.rate.value;

alert(hours * rate);
}

}
}



And
here is the HTML it is attached
to:



            html>




name="hours">
type="text" id="rate" name="rate">
name="calculate" value="Calculate">

id="savings">




name="hours">
type="text" id="rate" name="rate">
name="calculate" value="Calculate">
id="savings">







I'm
sure this is a really basic question but the solution is completely eluding me at this
point.



Here is the demo of the problem I'm
experiencing: rel="nofollow">http://jsfiddle.net/KcNWy/



Answer




You have the Closure Loop Problem. See, I
don't know, href="https://stackoverflow.com/questions/3953746/javascript-closure-stores-value-at-the-wrong-time">this
or about a million other SO questions caused by the same
gotcha.



i and
e are the same variables each time around the loop: the loop
merely changes i and e, it doesn't
give you a new e. So when the function() { ...
}
fires, long after the loop has finished iterating,
e is left with the final value it had, the last matching
input.




(Also, you haven't declared
var i, so the i is worse than that,
it's an accidental global.)



The typical way
around it is to add another function scope to retain a copy of the value at creation
time, in a
closure:



e.elements.calculate.onclick
= function(e) {
return function() {
var hours =
e.elements.hours.value;
var rate = e.elements.rate.value;

alert(hours * rate);

}

}(e);


You
can do it more cleanly in ECMAScript Fifth Edition with the
Function#bind
method:



e.elements.calculate.onclick
= function(e) {
var hours = this.elements.hours.value;
var rate =
this.elements.rate.value;
alert(hours *
rate);
}.bind(e);



but
that means href="https://stackoverflow.com/questions/2025789/preserving-a-reference-to-this-in-javascript-prototype-functions/2025839#2025839">adding
support for this method to existing browsers that don't support it
yet.



Alternatively putting the closure in the
loop mechanism itself avoids the
problem:



forEach(document.forms,
function(form) {
form.elements.calculate.onclick = function() {

var hours = form.elements.hours.value;
var rate =
form.elements.rate.value;

alert(hours * rate);

}
});

function forEach(list, callback) {
for
(var i= 0, n= list.length; i
callback(list[i]);
}



Alternatively
alternatively, just use the same function each time and grab your form reference from
the clicked element:



for (var i=
document.forms.length; i-->0;)

document.forms[i].elements.calculate.onclick=
calculate_click;

function calculate_click() {
var hours
= this.form.elements.hours.value;
var rate =
this.form.elements.rate.value;
alert(hours *
rate);
}



No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print ...