views:

762

answers:

2

When clicking on each div it should alert '1' if div 1 was clicked on or '5' if div 2 was clicked on. I have tried to make this code as easy to as possible because this is needed in a much larger application.

<html>
<head>
<style type="text/css">
#div1 { background-color: #00ff00; margin: 10px; padding: 10px; }
#div2 { background-color: #0000ff; margin: 10px; padding: 10px; }
</style>
<script type="text/javascript">

function init()
{
  var total = 1;

  var div1 = document.getElementById('div1'),
      div2 = document.getElementById('div2');

  var helper = function(event, id)
  {
      if (event.stopPropagation) event.stopPropagation();
      if (event.preventDefault) event.preventDefault();

      alert('id='+id);
  }

  div1.addEventListener('click', function(event) { helper(event, total); }, false);

  total += 4;

  div2.addEventListener('click', function(event) { helper(event, total); }, false);

}

</script>
</head>

<body onload="init();">

<div id="div1">1</div>
<div id="div2">2</div>

</body>
</html>

Thanks for your help! :-)

+3  A: 

The problem is that the event listeners and 'total' both exist in the same scope (init())

The event functions are always going to reference total within the init() scope, even if it is changed after the event functions are declared

To get around this, the event functions need to have a 'total' in their own scope which will not change. You can add another layer of scope using an anonymous function

For example:

(function (total) {
 div1.addEventListener('click', function(event) { helper(event, total); }, false);
}(total));

total += 4;

(function (total) {
  div2.addEventListener('click', function(event) { helper(event, total); }, false);
}(total));

The anonymous functions are passed init()'s current 'total' value as a parameter. This sets another 'total' to the anonymous function's scope, so it does not matter if init()'s total changes or not, because the event function will FIRST reference the anonymous function's scope.

Edit:

Also, you need to place a semicolon after the closing brace of the helper function, otherwise the script will complain that 'event' is undefined.

var helper = function(event, id)
{
  if (event.stopPropagation) event.stopPropagation();
  if (event.preventDefault) event.preventDefault();

  alert('id='+id);
};
Matt
Ahh. So effectively total is becoming like a variable pointer, much like in C++ variable*?
Gary Green
Actually, it's being copied into a local scope. If it were a pointer, then it would change both inside and outside of scope, which would bring you back to your original problem.
Matt
@Gary Matt's example is correct, here's a little terminology for you. You were running into your specific problem because of something known as "closure" (http://www.jibbering.com/faq/faq_notes/closures.html), which is a great and powerful feature of JavaScript. What Matt has shown you is how to get around the closure property (which sometimes gets in the way as you have seen) using anonymous functions and a technique known as currying. See http://ejohn.org/blog/partial-functions-in-javascript/ and http://stackoverflow.com/questions/1413916/.
Justin Johnson
+1  A: 

It's almost the same but i think it will be better div1.addEventListener('click', function(t){ return function(event) { helper(event, t); }}(total), false);

instead of

(function (total) { div2.addEventListener('click', function(event) { helper(event, total); }, false); }(total));

m4roo