in jquery 1.4.2, ff 3.6.6:
The following code produces three divs, which write messages to the firebug console as you would expect. However, if you unment out the loop and ment out the 3 lines doing it manually, it doesn't work - mousing over any of the divs results in "three"
being written to the console.
Why are these two methods any different than each other? In each one you use a selector to find the element and add an event to it.
<head>
<script type="text/javascript" src="/media/js/jquery.js"></script>
<script>
$( document ).ready( function() {
$("#one").mouseenter(function(){console.log("one")})
$("#two").mouseenter(function(){console.log("two")})
$("#three").mouseenter(function(){console.log("three")})
// names=['one','two','three'];
// for (k in names){
// id=names[k]
// $("#"+id).mouseenter(function(){console.log(id)})
// }
})
</script>
</head>
<body>
<span id="one">ONE</span>
<p><span id="two">TWO</span></p>
<p><span id="three">THREE</span></p>
</body>
in jquery 1.4.2, ff 3.6.6:
The following code produces three divs, which write messages to the firebug console as you would expect. However, if you unment out the loop and ment out the 3 lines doing it manually, it doesn't work - mousing over any of the divs results in "three"
being written to the console.
Why are these two methods any different than each other? In each one you use a selector to find the element and add an event to it.
<head>
<script type="text/javascript" src="/media/js/jquery.js"></script>
<script>
$( document ).ready( function() {
$("#one").mouseenter(function(){console.log("one")})
$("#two").mouseenter(function(){console.log("two")})
$("#three").mouseenter(function(){console.log("three")})
// names=['one','two','three'];
// for (k in names){
// id=names[k]
// $("#"+id).mouseenter(function(){console.log(id)})
// }
})
</script>
</head>
<body>
<span id="one">ONE</span>
<p><span id="two">TWO</span></p>
<p><span id="three">THREE</span></p>
</body>
Share
Improve this question
edited Jul 20, 2010 at 10:01
Daniel Vassallo
345k72 gold badges512 silver badges446 bronze badges
asked Jul 20, 2010 at 6:22
fastmultiplicationfastmultiplication
3,1011 gold badge34 silver badges39 bronze badges
2
- Please start with fixing your HTML. You have two unclosed <p> tags. – Māris Kiseļovs Commented Jul 20, 2010 at 6:24
-
You should declare your variables unless you want them to be global variables.
var names = ['one', 'two', 'three'];
,for (var k in names) {
,var id
. – Matt Commented Jul 20, 2010 at 6:35
2 Answers
Reset to default 12You would be having a very mon closure problem in the for in
loop.
Variables enclosed in a closure share the same single environment, so by the time the mouseenter
callback is called, the loop will have run its course and the id
variable will be left pointing to the value of the last element of the names
array.
This can be quite a tricky topic, if you are not familiar with how closures work. You may want to check out the following article for a brief introduction:
- Mozilla Dev Center: Working with Closures
You could solve this with even more closures, using a function factory:
function makeMouseEnterCallback (id) {
return function() {
console.log(id);
};
}
// ...
var id, k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
id = names[k];
$("#" + id).mouseenter(makeMouseEnterCallback(id));
}
You could also inline the above function factory as follows:
var id, k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
id = names[k];
$("#" + id).mouseenter((function (p_id) {
return function() {
console.log(p_id);
};
})(id));
}
Any yet another solution could be as @d.m suggested in another answer, enclosing each iteration in its own scope:
var k,
names = ['one','two','three'];
for (k = 0; k < names.length; k++) {
(function() {
var id = names[k];
$("#" + id).mouseenter(function () { console.log(id) });
})();
}
Although not related to this problem, it is generally remended to avoid using a for in
loop to iterate over the items of an array, as @CMS pointed out in a ment below (Further reading). In addition, explicitly terminating your statements with a semicolon is also considered a good practice in JavaScript.
This happens because when the code of the anonymous function function(){console.log(id)}
is executed, the value of id
is three
indeed.
To can use the following trick to enclose the value on each loop iteration into the anonymous callback scope:
names=['one', 'two', 'three'];
for (var k in names) {
(function() {
var id = names[k];
$("#"+id).mouseenter(function(){ console.log(id) });
})();
}