1

I need to use bind the click event to an element in the DOM whilst being able to pass arguments on the fly, including the event object. My current script uses the following:

var counter = 1;
$(".dynamo_user_info .dynamo_awards").each(function(){
    $(this).find("~ div a").each(function(){
        var id = $(this).attr("rel").split("aid-")[1];
        $(this).attr("id","dynamo_award-"+counter+"-"+id).bind('click',{c:counter,i:id},function(e){
            e.returnValue = e.preventDefault && e.preventDefault() ? false : false;
            dynamo.awards.tooltip.ini(c,i);
        });
    });
    counter++;
});

Now, as you can see, counter increases on each iteration and id does not hold a static value. After each iteration, the final values are: counter = 4, id = 2. Now whenever one of these elements is clicked, it preventDefaults as appropriate, then runs dynamo.awards.tooltip.ini(c,i);.

However, this does not work as intended. Instead of it running:

dynamo.awards.tooltip.ini(1,1); dynamo.awards.tooltip.ini(2,6);

etc (for example), it instead runs:

dynamo.awards.tooltip.ini(4,2); dynamo.awards.tooltip.ini(4,2);

i.e it uses the last stored values for counter and id. My guess is it does this due to how bind works, passing the parameters when the element is clicked, not before. I'm not sure how to fix this, unless I can trick the scope of the function in some way?

Keir Simmons
  • 1,634
  • 7
  • 21
  • 37
  • 2
    *Whilst!* Ahh, the English. `:)` – Jared Farrish Aug 12 '12 at 22:14
  • You need to understand [closure scope](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work). (See also [Bonsai!](http://bonsaiden.github.com/JavaScript-Garden/#function.closures) and boring old [MDN](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures)) – Jared Farrish Aug 12 '12 at 22:17
  • just as a side note, this line seems redundant: `e.returnValue = e.preventDefault && e.preventDefault() ? false : false;` – ahren Aug 12 '12 at 22:27
  • I've been reading up on closure for a good few hours today but not understanding it well enough to abuse it. – Keir Simmons Aug 12 '12 at 22:29
  • You can just use `e.preventDefault()`. jQuery passes you a jQuery event, not the browser event. jQuery events always have `e.preventDefault()` and will in fact do this plumbing behind the scenes. – Esailija Aug 12 '12 at 22:30
  • @ahren e.preventDefault() failed in some version of IE during testing (though said testing took place a few years back on an older version of jQuery). – Keir Simmons Aug 12 '12 at 22:30
  • 1
    @KeirSimmons You probably didn't use jQuery events, jQuery has normalized `.preventDefault` since 1.0 6 years ago – Esailija Aug 12 '12 at 22:33
  • @Esailija Fair point, I'll remove the redundancy. – Keir Simmons Aug 12 '12 at 22:39
  • @JaredFarrish Would you be able to give me a pointer on how to use closure in this case? – Keir Simmons Aug 12 '12 at 22:42
  • 1
    Add `var c = counter, i = id;` to the `$.find().each()` scope, above the `$.click()` scope. This will maintain it's value in scope, you don't have to *bugger* (`!`) with the other *cruft* (`!`). Only thing, I'd use more expressive syntax, say `var self_counter = counter, self_id = id;`. – Jared Farrish Aug 12 '12 at 22:46
  • Here's me [abusing closures](http://jsfiddle.net/userdude/kS3FG/) and [more abuse](http://jsfiddle.net/userdude/S4ftm/) and yet I [take no mercy](http://jsfiddle.net/userdude/SCKxf/) – Jared Farrish Aug 12 '12 at 22:54
  • @JaredFarrish It works perfectly, but I still can't see why. I can't get past why, without the initial `var` declaration, the scope is somehow different. Can you tell me exactly what the line `var self_counter = counter, self_id = id;` does explicitly? – Keir Simmons Aug 12 '12 at 23:02
  • Your comment is a little mangled, but it keeps the variable's value in scope, which is important. People will bitch if I try to explain it plainly, so read those links I provided. Seriously, closures are one of the power tools in Javascript. They're *brilliant*. – Jared Farrish Aug 12 '12 at 23:04
  • See this fiddle: http://jsfiddle.net/DLz92/1/ For comments *and* the **Whoa!** factor. Closures at work. (As well as the [related question/answer](http://stackoverflow.com/questions/9240479/javascript-to-make-a-fast-running-image-slideshow/9242332#9242332).) – Jared Farrish Aug 12 '12 at 23:06

1 Answers1

2

The arguments are accessible through the event.data object , like

dynamo.awards.tooltip.ini(e.data.c, e.data.i);

http://api.jquery.com/bind/#passing-event-data

Musa
  • 96,336
  • 17
  • 118
  • 137