1

I have a map of messages say:

var Mapping = {
"notnow": 2,
"expensive": 3,
"not_worth_it": 4
}

i have a bunch of html elements (lets say divs with the same name) so

<div id="notnow"></div>

,etc

now i want to attach a click handler to each of them, i run a loop as shown below

function setThemUp(){
   for(var item in Mapping)
   {
      $("#" + item).bind('click', function () {
      Apply(Mapping[item]); });
   }
}

But for some reason all of them seem to get bound to "not_worth_it":4. Not to their respective values.

I'm using Jquery 1.5.

Can someone please explain why this might be happening?

My guess is that instead of the Mapping[item] being resolved to their values, it's being passed as a reference or something, that's why since the value of item eventually points to "not worth it" all of them call the function with that value itself. Any way in which i could overcome them.

Hard coding each of them as

  $("#notnow").bind('click', function () {
      Apply(Mapping["notnow"]); });
  $("#expensive").bind('click', function () {
      Apply(Mapping["expensive"]); });
  $("#not_worth_it").bind('click', function () {
      Apply(Mapping["not_worth_it"]); });

does work, but i would prefer an elegant solution with a loop.

Answer

i went with the closure solution

function setThemUp(){
   for(var item in Mapping)
   {
       $("#" + item).bind('click', (function () {
            return function(temp) {
                 Apply(Mapping[temp]); };
            })(item));
       }
   }

Reasons being , this was more of a why the loop didn't work rather than about optimization of the jquery , since this was afterall a representative example, not my actual code, and this was an elegant solution to that problem.

2
  • Are you inserting elements dynamically? Commented Nov 16, 2012 at 18:10
  • You could just reference Mapping[this.id] in each of your click handlers. Also, it'd probably be better to give them all a class name so you can simplify the binding even further. Commented Nov 16, 2012 at 18:24

3 Answers 3

4

This is a classic case of scope issues: you're referencing the variable item in each of your bound handlers. The variable item changes, though -> it's being assigned all properties of the Mapping object literal, the last one being not_worth_it.

Creating a closure might help, to preserve the state of item for each callback:

for(var item in Mapping)
{
   $("#" + item).bind('click', (function(currentItem)
   {//IIFE, pass item as argument---------/
        return function ()
        {//return function, \/ access to closure scope
            Apply(Mapping[currentItem]);
         };
    }(item)););
}

But this seems to be somewhat overkill, why not simply delegate the event, and use Mapping[$(this).attr('id')]?

Sign up to request clarification or add additional context in comments.

3 Comments

I can't use $(this).attr('id') because the actual ids are a lot more convoluted that what is shown here, they need some processing, infact you can forget the ids all together, im more interested in the fact that the state of item is being preserved and that Mapping[item] is processed at the time of the event instead of the parameters being resolved when i bind them itself.
@BrijeshBharadwaj Well this answer solves your problem. Notice how the function passed to bind is executed immediately, and inside it returns the actual function to be executed when the item is clicked. This way, the state of the item variable is kept accurate per event
@BrijeshBharadwaj: you might want to think about using some data attribute to set the Mapping values, and delegate. After all, it's the most efficient way: one listener, one handler, all events. If not, a closure is what you need: the value of item is being passed to an IIFE, which returns the handler. The scope of the IIFE is not GC'ed and so the handler still has access to the value item HAD when the function was created
0

I would suggest moving to this form:

Add the class mapped to your mapping divs.

HTML

<div id="notnow" class="mapped"></div>

JS

function setThemUp(){
    $('.mapped').bind('click', function () {
        Apply(Mapping[this.id]); 
    });
}

Comments

-1

The problem is you need to write:

for (var item in Mapping) 

rather than foreach.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.