在 for 循环中分配单击处理程序

我有几个 div 的 #mydiv1#mydiv2#mydiv3,... 并希望分配点击处理程序给他们:

$(document).ready(function(){
for(var i = 0; i < 20; i++) {
$('#question' + i).click( function(){
alert('you clicked ' + i);
});
}
});

但是,当点击 #mydiv3时(每隔一次点击) ,不显示 'you clicked 3',而是显示 'you clicked 20'。我做错了什么?

59384 次浏览

It's a common mistake to create closures in loops in Javascript. You need to have some sort of callback function like this:

function createCallback( i ){
return function(){
alert('you clicked' + i);
}
}


$(document).ready(function(){
for(var i = 0; i < 20; i++) {
$('#question' + i).click( createCallback( i ) );
}
});

Update June 3, 2016: since this question is still getting some traction and ES6 is getting popular as well, I would suggest a modern solution. If you write ES6, you can use the let keyword, which makes the i variable local to the loop instead of global:

for(let i = 0; i < 20; i++) {
$('#question' + i).click( function(){
alert('you clicked ' + i);
});
}

It's shorter and easier to understand.

To clarify, i is equal to 20 because the click event won't have fired until after the loop has finished.

Generally, if you are looking to assign click handles to a large number of items, you want to have a container (higher level div) that interprets the clicks for you, as the click bubbles up from the dom.

<div id="bucket">
<span class="decorator-class" value="3">
...
</div>


<script>
$(document).ready(function(e){
$("#bucket").live('click', function(){
if(e.target).is('span'){
alert("elementid: " + $(e.target).val());
}
}
}
<script>

You can get by with assigning the click handler once (or at least not making many unnecessary closures). Put all the divs in one class mydivs, then:

$(document).ready(function(){
$('.mydivs').click(function(){
// Get the number starting from the ID's 6th character
// This assumes that the common prefix is "mydiv"
var i = Number(this.id.slice(5));


alert('you clicked ' + i);
});
});

This looks at the element's ID to get its number, using the slice string method to strip the initial letters off.

Note: It may be better to use

$('#divcontainer').on('click', '.mydivs', function(){

instead of

$('.mydivs').click(function(){
$(document).ready(function(){
for(var i = 0; i < 5; i++) {
var $li= $('<li>' + i +'</li>');
(function(i) {
$li.click( function(){
alert('you clicked ' + i);
});
}(i));
$('#ul').append($li);
}
});

Using on to attach the 'click' handler you can use the event data in order to pass your data like in:

for(var i = 0; i < 20; i++) {
$('#question' + i).on('click', {'idx': i}, function(e) {
alert('you clicked ' + e.data.idx);
});
}

//
// let's creat 20 buttons
//


for(var j = 0; j < 20; j++) {
$('body').append($('<button/>', {type: 'button', id: 'question' + j, text: 'Click Me ' + j}))
}


//
// Passing data to the handler
//
for(var i = 0; i < 20; i++) {
$('#question' + i).on('click', {'idx': i}, function(e) {
console.log('you clicked ' + e.data.idx);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>