1

I am working with leaflet (v 0.7.7) and I have an Ajax call that gets some server data to bind to my map in the form of clickable text labels. In the loop where I am binding the JSON data that I got from the server I have the following code:

var item_name = L.marker([data.X, data.Y],
{
    icon: L.divIcon(
    {
        className:'MapTag', 
        iconAnchor: [10, 10],
        html:'<img src="/Images/Map/Item' + data.Id + '.png">' + data.Name 
    })
}).on('click', onItemClick(data.Id));

item_layer.addLayer(item_name);

Elsewhere, I have the onItemClick code:

function onItemClick(item_id)
{
    alert(item_id);
}

Now, the good news, if I comment out the event binding part of that, the loop completes, and leaflet behaves as it should. However, when I bind the click event, things get odd. The event is fired once for each item in the collection I am binding to. When the data is loaded from the server, I get an alert popping up for each time through the loop with the current item's Id. It feels like it is being fired 'onload' rather than 'onclick'. To top it off, it also doesn't register clicks on the divIcons after loading.

There must be something I am missing here, but I cannot see what it is. Any suggestions?

THis question is similar to (Marker in leaflet, click event)

Resolution: I changed the last line of the divIcon declaration to:

}).on('click', function(e){ alert(data.Id); });

This now works as intended. I am guessing that the strange binding behavior is a result of not binding a defined method reference and leaflet having some sort of functional meltdown in its event management code.

I kept the 'e' argument in, since it is what the leaflet docs show. I am not using it, but if someone else copy-pastes this, they might need it.

1 Answer 1

3

You're confusing the concepts of function references and function calls, and you are not making a closure over the item ID.

If you declare this:

function onItemClick(id) { alert(id); }

This prints the reference to the function:

console.log( onItemClick );

And this prints the return value of calling that function (as it returns nothing, this equals undefined):

console.log( onItemClick(5) );

So when you're doing this:

L.marker(....).on('click', onItemClick(id) );

the function gets called, and on() receives the return value of that function, i.e.:

L.marker(....).on('click', undefined );

What you want to do is have a function that returns a function:

function onItemClick(id) { return function(){ alert(id); } }

This way, when you do

L.marker(....).on('click', onItemClick(5) );

That will make a function call, and use the return value, which now looks like this:

L.marker(....).on('click', function() { alert(5); } );
3
  • Wow, mind blown. I was looking at it for a couple of hours and you come along and nail it. Thanks for the assistance.
    – Roger Hill
    Commented Feb 16, 2016 at 10:42
  • I just noticed something. Because the function we are mapping to the click event is a closure, when I put this code into a loop, all the items are mapped to the same Id. (All the items look at the Id, which after the loop terminates is the last Id) I was trying to write out the function call as a string to the tag so that each leaflet tag wasn't holding a reference to the same value. I need to re-think this.
    – Roger Hill
    Commented Feb 17, 2016 at 20:32
  • No - the point of closures is to prevent exactly that, by defining a variable scope of their own. I recommend that you read up documentation about closures - I know they're tricky to master. Commented Feb 17, 2016 at 20:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.