News for hackers. Stuff that matters.
Re:JavaScript Interview Question by Wilduck on Thursday January 20, 2011 @12:32PM (#2124396)
Please do.
Re:JavaScript Interview Question by lhorie on Thursday January 20, 2011 @12:57PM (#2124532)
this is what I usually tell people to do:

  var els = document.getElementsByTagName('li');//use var unless you really really really meant global
  for(var i=0; i < els.length; i++){
    (function(i) {//creates new scope for each iteration
      els[i].addEventListener('click', function(){alert(i);}, false);
    })(i);
  }
Re:JavaScript Interview Question by aaronblohowiak on Thursday January 20, 2011 @01:05PM (#2124565)
i agree with the choice of code, but I have a different style for laying out the code.

i dislike the declaration and immediate execution of an anonymous function. i much prefer to give it a name, move it out of the loop itself and then call it within the loop. it makes reading the code more top-to-bottom.

with this approach, when I get to the anon func declaration, i have to scan down looking for when it is invoked. while indentation helps, i then have to backtrack up to the beginning of the func declaration.. and hopefully i haven't lost track of what the execution context for the anon func is and so forth...

Re:JavaScript Interview Question by lhorie on Thursday January 20, 2011 @01:43PM (#2124729)
I don't generally care too much to talk about style when the problem at hand is that someone has been banging their head against the desk for the past two hours and they just want it to work (and, in my experience, this usually is the case, for some reason)

But yeah, if we're gonna be super nitpicky, yeah sure, improve the for loop also, while we're at it.

  for (var i = -1; els[++i];) {//given the code in the loop body, this is shorter and faster
Re:JavaScript Interview Question by heed on Thursday January 20, 2011 @02:50PM (#2125010)
Wouldn't var i have to be set to -1:

  for (var i = -1; els[++i];)  ?
Otherwise, the alert won't trigger on the first li element.
Re:JavaScript Interview Question by lhorie on Thursday January 20, 2011 @02:56PM (#2125047)
Hah, good catch. Was thinking of i++ for some reason. Fixed.
Re:JavaScript Interview Question by kenjackson on Thursday January 20, 2011 @01:37PM (#2124705)
Does this work (from a non-JS person -- I like reading JS books, but don't code in it at all). This is certainly less elegant, but seems easier to follow:

  var els = document.getElementsByTagName('li');

  for(i=0; i < els.length; i++){
    var j = i;
    els[j].addEventListener('click', function(){alert(j);}, false);
  }
Re:JavaScript Interview Question by lhorie on Thursday January 20, 2011 @01:56PM (#2124791)
No, it doesn't because j is in the same scope as i.

Your confusion is because of a javascript quirk

  for (...) {
    var foo = bar
  }
is actually the same as

  var foo
  for (...) {
    foo = bar
  }
Notice how you only ever have one foo

What you need is:

  for (...) {
    (function(bar) {
       var foo = bar
    })(bar)
  }
which create a new foo every time you loop
Re:JavaScript Interview Question by buerkle on Thursday January 20, 2011 @01:45PM (#2124744)
That doesn't work. Variables in javascript are not block scoped but function scoped.
Re:JavaScript Interview Question by kenjackson on Thursday January 20, 2011 @01:52PM (#2124774)
Thanks. Good to know.
Re:JavaScript Interview Question by theoretical on Thursday January 20, 2011 @12:44PM (#2124456)
It won't alert quite what you want - when you click on each element in the list, it will alert "4".

This is because the function assigned to the click event of each li is bound to the "i" variable used in the loop by a closure. Variable i is incremented to 4 before the loop ends, so that is what each will print.

You could use make the function used in the click handler take a parameter, then use partial function application (or currying) to fix the value of the parameter.

Re:JavaScript Interview Question by Timmy_C on Thursday January 20, 2011 @12:51PM (#2124501)
The function in the click handler does take a parameter 'e' (...or whatever you call it) which is an Event object. But I think you're on to something by using currying.
Re:JavaScript Interview Question by theoretical on Thursday January 20, 2011 @01:03PM (#2124559)
Well yes, the function is called with a MouseEvent as one of the arguments, so you could use the "arguments" object to access it.

What I was thinking of, was something along the lines of:

  // From http://ejohn.org/blog/partial-functions-in-javascript/
  Function.prototype.curry = function() {
    var fn = this, args = Array.prototype.slice.call(arguments);
    return function() {
      return fn.apply(this, args.concat(
        Array.prototype.slice.call(arguments)));
    };
  };

  els = document.getElementsByTagName('li');
  for(i=0; i < els.length; i++){
    els[i].addEventListener('click', function(x){
        alert(x);
    }.curry(i), false);
  }
Re:JavaScript Interview Question by robterrell on Thursday January 20, 2011 @12:49PM (#2124491)
Wrap the line of code inside the loop inside another function, which will have the effect of binding the iterator i to the desired value before the event handler anonymous function is created.