1

I have a preloaded page and I am trying to add onclick event to all the nodes with a specific class name. But the function in the onclick should use the element node in it.

var elemArr=document.getElementsByClassName('ABC');

    for(var i=0;i<elemArr.length;i++){

        elemArr[i].onclick = function()
        {
            console.log(elemArr[i]);  // This is being returned as undefined
            // How can I use elemArr[i] here 
        };
    }

I tried

for(var i=0;i<elemArr.length;i++){

    printObj=elemArr[i];
    elemArr[i].onclick = function()
    {
        console.log(printObj);
        var newObject = jQuery.extend(true, {}, printObj);
        console.log(newObject );
    };
}

but didn't work.I feel the above won't work anyway. How do I solve this problem..?

Srinath Mandava
  • 3,384
  • 2
  • 24
  • 37
  • You have to use JavaScript Closures: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Closures – Karlen Kishmiryan Dec 11 '14 at 15:00
  • All your handlers are sharing the same `i` variable, which by the time your handler is called, will have the value of `elemArr.length` See the linked question. Simplest solution `for(var i=0;i – Ruan Mendes Dec 11 '14 at 15:02
  • 1
    @KarlenKishmiryan The OP is already using closures ;) The problem is that the OP is sharing a closure – Ruan Mendes Dec 11 '14 at 15:06
  • For consistency in code, when using jQuery, use it ;) – A. Wolff Dec 11 '14 at 15:07
  • ... and you would automatically avoid closure problem with jQuery. – dfsq Dec 11 '14 at 15:08
  • @dfsq You wouldn't automatically fix it `$(elemArr[i]).click(function() {console.log(elemArr[i]); };` – Ruan Mendes Dec 11 '14 at 15:10
  • @JuanMendes But that's not the way you should code it using jQuery – A. Wolff Dec 11 '14 at 15:10
  • @JuanMendes I can't see any closure OP used. In my answer I have created a demo using closures, which works fine. – Karlen Kishmiryan Dec 11 '14 at 15:11
  • @JuanMendes I mean proper jQuery usage: `$('.ABC').click(function() { console.log(this) })` :) – dfsq Dec 11 '14 at 15:11
  • @dfsq My example shows that just using jQuery doesn't fix it. "Proper" JavaScript usage also fixes it – Ruan Mendes Dec 11 '14 at 15:11
  • @KarlenKishmiryan All the event handlers use `i` from a closure – Ruan Mendes Dec 11 '14 at 15:12
  • @KarlenKishmiryan Where the word `function` there is a closure. Always :) – dfsq Dec 11 '14 at 15:12
  • Hmm.. but the `onclick` handler is being attached to `elemsArr[i]`, not to the `printObj`. So what's the purpose of `printObj`? – Karlen Kishmiryan Dec 11 '14 at 15:13
  • @dfsq Not always, only when there is a function inside of a function. Also, note that if the inner function doesn't use any of the variables in the outer function, Chrome does not create a closure. http://stackoverflow.com/a/14987697/227299 – Ruan Mendes Dec 11 '14 at 15:13
  • @JuanMendes `$('.ABC').on('click', function(){console.log(this);});` What's wrong there? – A. Wolff Dec 11 '14 at 15:13
  • @A.Wolff Nothing, you could also use `this` in plain JavaScript, but you can't use `i` from the closure even in jQuery. I'm just saying there are ways to botch this, even with jQuery as I've shown in my example – Ruan Mendes Dec 11 '14 at 15:15
  • @JuanMendes Always. Closure is a function with the scope it was created in. So any function has access to some scope. This access is a closure. – dfsq Dec 11 '14 at 15:15
  • @dfsq Did you see the link I posted? Local variables go on the stack, and a closure is only created (plain local variables are on the stack) when you have an inner function. Be sure to look at the post I linked – Ruan Mendes Dec 11 '14 at 15:17
  • @JuanMendes I think we are arguing about definitions. You are talking about something else probably. – dfsq Dec 11 '14 at 15:24
  • No, I'm just saying that JavaScript still uses a stack for local variables, closures are only needed to enclose local variables that would otherwise go out of scope (not in your current stack frame), functions within functions. The chrome debugger shows three different scopes: local (stack), closures, and global. – Ruan Mendes Dec 11 '14 at 15:28
  • @JuanMendes Thx, very instructive! – A. Wolff Dec 11 '14 at 16:14

1 Answers1

7

You should wrap your code inside of loop with a function so that a new copy of i is created for each of your event handlers, like this.

var elems = document.getElementsByClassName('elem');

if (elems.length) {
  for (var i = 0, l = elems.length; i < l; i++) {
    (function(i) {
      elems[i].onclick = function() {
        alert('Element #' + i);
      }
    })(i);
  }
}

// Could also use bind for slighlty cleaner code
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

for (var i = 0, l = elems.length; i < l; i++) {
  elems[i].onmousedown = (function(index, e) {
    console.log('MouseDown - Element  #' + index);
  }).bind(elems[i], i);
}
<div class="elem">Elem #1</div>
<div class="elem">Elem #2</div>
<div class="elem">Elem #3</div>
Karlen Kishmiryan
  • 7,322
  • 5
  • 35
  • 45