JavaScript Scope

This is an issue that popped up with a colleague yesterday. Roughly, there was some code like this.

  function setUpStuff() {
    var items = cssQuery("#stuff p");
    for (var i = 0; i < items.length; i++) {
      var item = items[i];
      item.onclick = function() {
        alert("item is " + item);
      };
    }
  }

Unfortunately, even if cssQuery() returns a list of three items, every call to alert() will show the last item. Why?

It’s all down to the fact that JavaScript doesn’t have block-scoped variables. It only has function-scope.

In the context of the code above, it means that item exists from the point of entry to setUpStuff(). It’s just undefined until the we get into the loop. The consequence of this is that each time we assign onclick, we’re referring to the same variable.

The solution is to create another function scope, of course.

  function setUpStuff() {
    var items = cssQuery("#stuff p");
    for (var i = 0; i < items.length; i++) {
      setUpAStuff(items[i]);
    }
  }
 
  functon setUpAStuff(item) {
    item.onclick = function() {
      alert("item is " + item);
    };
  }

This now works as expected, because inside setUpAStuff(), we’re referring to a different variable each time (you could also use an anonymous function). This is definitely one of the more confusing areas of JavaScript (though JSLint does pick up on this).

* Block-Structured JavaScript
* Core JavaScript 1.5 Guide:Block Statement (on devmo)

5 thoughts on “JavaScript Scope

  1. dom

    @märtin: You’re absolutely correct. But this is trivial example to demonstrate scope within JavaScript, not show how to access stuff within events.

    Thanks for the tip though, I’ll have to remember that one. :)

  2. märtin

    when you just want to alert the item (element) to which the event is attached you can easily refer to “this” (or some property of your event object, depending on toolkit used) inside the event function. the attempt to pass an element to its own event seems pointless to me. When i need to access some data from within an event i usually fill the element’s rel-attr with a key(number) needed to access the corresponding data from some array/obj.

    bäst

    märtin

  3. dom

    @Simon: I certainly could do that (and probably would in my own code, though I confess it doesn’t scan that well to me). But it’s an aside here. The main point is that JavaScript doesn’t give you new scope when you open up a pair of curlies.

  4. dom

    Please excuse the direct assignment to onclick. Under normal circumstances I would use some form of addEvent()

Comments are closed.