I have a question about the .click function in Jquery. I have this code:
for (var i = 0; i < 5; i++) {
var divTest = $('<div></div>');
divTest.text("My Div " + i);
divTest.click(function() {
alert("Alert: Div " + i);
});
$('#myTest').append(divTest);
}
I expected to add five divs to the "myTest" element and for each div the onclick function would show an alert with the corresponding div number.
The divs were added properly, but when I click on the divs I always get the alert with the text: "Alert: Div 5". Why? What I have to change to generate the behavior that I'm expecting?
Here is my jsFiddle: http://jsfiddle.net/BKFGm/2/
In this case you should use closure:
(function(i) {
divTest.click(function() {
alert("Div: " + i);
});
})(i);
DEMO: http://jsfiddle.net/BKFGm/4/
Another option is to pass i in the eventData map:
divTest.click({ i: i }, function(e) {
alert("Div: " + e.data.i);
});
DEMO: http://jsfiddle.net/BKFGm/11/
Once again, a classic case of closures. i keeps getting incremented, whereas you want to anchor it in the click event. Try this:
for( i=0; i<5; i++) {
(function(i) {
// your code that depends on `i` here
})(i);
}
This is a scope issue. Also, it's a very commonly asked question here.
The simple fix:
for(var i = 0; i < 5; i++){
var divTest = $('<div></div>')
divTest .text("My Div " + i);
(function(index){
divTest .click(function () {
alert("Div: " + index);
});
})(i);
$('#myTest').append(divTest);
}
When the Alert happens, the variable i is already set to 5.
.click works in a different scope than your cycle and it is undefined when your click handler is executed, unless you have an other i variable on the global scope.
You can also try this
$(function() {
for(var i = 0; i < 5; i++){
var divTest = $('<div/>', {
'text':"My Div " + i,
'click':(function(i){
return function(){
alert("Div: " + i);
}
})(i)
});
$('#myTest').append(divTest);
}
});
DEMO.
Related
I'm having several div's #mydiv1, #mydiv2, #mydiv3, ... and want to assign click handlers to them:
$(document).ready(function(){
for(var i = 0; i < 20; i++) {
$('#question' + i).click( function(){
alert('you clicked ' + i);
});
}
});
But instead of showing 'you clicked 3' when click on #mydiv3 (as for every other click) I get 'you clicked 20'. What am I doing wrong?
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.
$(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>
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(){
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>
I'm having several div's #mydiv1, #mydiv2, #mydiv3, ... and want to assign click handlers to them:
$(document).ready(function(){
for(var i = 0; i < 20; i++) {
$('#question' + i).click( function(){
alert('you clicked ' + i);
});
}
});
But instead of showing 'you clicked 3' when click on #mydiv3 (as for every other click) I get 'you clicked 20'. What am I doing wrong?
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.
$(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>
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(){
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>
I need a click function in a for loop, so every id element is clickable. But I also need the i in the click function, that's why I thought a self executing anonymous function would be the best way to do so. But for some reason this is not working, maybe because the click function doesn't allow me to forward a parameter? What have I done wrong?
for (var i = 0; i < countItems; i++) {
$("#item-" + i).click(function(idx) {
alert(idx);
})(i)
}
The self executing function must return a function:
for (var i = 0; i < countItems; i++) {
$("#item-" + i).click(function(indx){
return function(){ //must return a function
alert(indx);
}
}(i));
}
JS Fiddle: http://jsfiddle.net/HuHXr/
As a side note, using bind() javascript method:
for (var i = 0; i < countItems; i++) {
$("#item-" + i).click(function(indx){
alert(indx);
}.bind($("#item-" + i)[0],i));
}
you can try something like this
for (var i = 0; i < countItems; i++) {
$("#item-" + i).click(clickFunctn);
}
function clickFunctn(obj){
var i=$(obj).attr('id').split('-')[1];
alert(i);
}
In this way you will optimize the code and your 'i' will be with you as well, and all items are clickable. And you are just binding one handler function.
for (var i = 0; i < countItems; i++) {
(function(i){
$("#item-" + i).click(function(idx) {
alert(idx);
});
})(i);
}
Also note that idx is the event object.
Fiddle: http://jsfiddle.net/2DRLx/
I'm having several div's #mydiv1, #mydiv2, #mydiv3, ... and want to assign click handlers to them:
$(document).ready(function(){
for(var i = 0; i < 20; i++) {
$('#question' + i).click( function(){
alert('you clicked ' + i);
});
}
});
But instead of showing 'you clicked 3' when click on #mydiv3 (as for every other click) I get 'you clicked 20'. What am I doing wrong?
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.
$(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>
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(){
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>
I'm having several div's #mydiv1, #mydiv2, #mydiv3, ... and want to assign click handlers to them:
$(document).ready(function(){
for(var i = 0; i < 20; i++) {
$('#question' + i).click( function(){
alert('you clicked ' + i);
});
}
});
But instead of showing 'you clicked 3' when click on #mydiv3 (as for every other click) I get 'you clicked 20'. What am I doing wrong?
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.
$(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>
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(){
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>