Reputation: 101
I'm trying to copy the value from "FROM" fields into "TO" fields. My first attempt was this:
function updateToField(toField,fromField)
{
toField.value = fromField.value}
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,fromField)};
}
}
The website has several FROM-TO pairs, and this only seem to work for the last pair in the "inputs" array.
Then I tried this:
function updateToField(toField,fromField)
{
toField.value = fromField.value}
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,this)};
}
}
With this, when any FROM field in the page is modified, it's copied to the last TO field on the page. I think this is one of those issues I've read about parameters as value or reference, but I can't figure it out. Also this is a VERY simplified version of the code, I actually fill the inputs list with a getElementsByClass function and must search through childnodes. Does anyone have a clue on whats going on?
Upvotes: 3
Views: 26118
Reputation: 44386
That closure, I do not think it means what you think it means.
This line here:
fromField.onchange = function(){updateToField(toField,this)};
means "assign to onchange
a function that assigns the contents of that fields to whatever toField
is at the time of the change!
Since you only have one variable toField
all the changeable fields will be assigned to it.
This would work:
var setOnChange = function(fromField, toField) {
fromField.onchange = function(){updateToField(toField,this)};
};
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
setOnChange(inputs[j], inputs[j+1]);
}
}
EDIT: Isaac might have a better explanation of the problem (although I don't really like his solution).
Upvotes: 1
Reputation:
The problem actually has to do with scope. What's happening is that your functions (the ones you assigned to onchange
) are capturing the variables toField
and fromField
and their values keep changing. I know it looks like you've declared them anew each time through the loop, but that's not how JS works; consecutive trips through a loop share a scope, so fromField
is the same variable each time through and you're merely assigning it a new value in each iteration. So at the end, all of your functions refer to the same fromField
variable. And that fromField
variable, naturally, contains the last value you assigned it.
So when you eventually call all of those functions, they all do the same thing, because all of their fromField
s (and, by the same logic, toField
s) are the same variable. So that explains why only the last inputs worked; they're what fromField
and toField
contained when you ran the functions.
You can fix this by introducing an intermediate function, since functions do create new scopes. That way each time through the loop, you get brand new variables.
function updateToField(toField,fromField)
{
toField.value = fromField.value;
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
function(){
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,fromField)};
}
}();
}
Upvotes: 1
Reputation: 41600
That's is because of how closures work. When you assign the onchange
function you create (for every loop) a new anonymous function that calls updateToField
, but the value of the toField
and this
parameters (or any other you are passing) are not bind to the "current" loop, instead that parameters are bind to the last value of the loop (that's why is only working with your last "TO").
Instead of assigning a new function to onchange
property try calling the Function.bind
if you're running in an environment that has that or write one if you don't have it.
Here are the documentation for bind
So you can go like this:
fromField.onchange = updateToField.bind(this, fromField, toField);
Or you can use the other approach that Malvolio wrote.
Upvotes: 1