Clickable LabeledMarker

Back in January, I demonstrated how to extend GMarker for text-based labels. In the interests of keeping things simple, I ignored any event handling, and just covered the nuts and bolts of how to make a wrapper class in JavaScript, and how to add the basic functionality we wanted.

Well, we had a number of requests by email for an explanation of how to set this up with clickable markers, so here it is. First, I’ve made a new version of the demo, which now features the traditional infoWindow popups, triggered by either marker clicks or selection from the sidebar.

LabeledMarker Click

If you just want the source for the new version, it’s right here. There’s a brief discussion of the changes, below the jump.

Constructor

In the original LabeledMarker constructor, there were a handful of or-blocks that could take advantage of the new entities in the GMarkerOptions array. We need to change this slightly, now, so that we can keep track of whether or not we’re supposed to be a clickable marker. Also, since this version still doesn’t support dragging, I’m adding an if-block to specifically disallow it:

this.clickable = options.clickable || true;
if (options.draggable) {
    // This version of LabeledMarker doesn't support dragging.
    options.draggable = false;
}

Nothing too scary there. Basically, if the programmer tries to instantiate a draggable LabeledMarker, we change it back on them. There are better ways this error could be handled, but this is reasonably graceful and doesn’t waste a lot of bytes.

GEvent Initialization

The real meat of this happens in the initialize method; that’s where all the markup comes into play, and that’s where our event handlers need to take shape. The thing is, though, we’re really not trying to do anything too complicated here—all we want is for any event that happens on our new div to we passed through to the marker. In fact, it’s as simple as setting up a loop to iterate through known events:

if (this.clickable) {
        var eventPassthrus = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'];
        for(var i = 0; i < eventPassthrus.length; i++) {
                var name = eventPassthrus[i];
                GEvent.addDomListener(div, name, newEventPassthru(this, name));
        }

        // Mouseover behaviour for the cursor.
        div.style.cursor = "pointer";
}

The newEventPassthru function just returns a simple handler:

function newEventPassthru(obj, event) {
        return function() {
                GEvent.trigger(obj, event);
        };
}

This code doesn’t pass through any arguments of the event, but none of the GMarker events have arguments anyways, so this is no loss. If the spec were to change, and onclick (for example) began returning a coordinate pair for where it was clicked, this implementation would have to be revisited.

Clean Up

The remove function now has an extra line, to zap all those extra event handlers, when the marker gets deleted. We could have saved all the listener handlers ourselves, but why bother, when this sweet automatic method does all the work for us?

GEvent.clearInstanceListeners(this.div);

Cleaning up like this may seem like a big deal over nothing, but it’s really important in order that code like the Marker Manager may function properly; the Manager depends on being able to reliably add and remove markers from a map, and not have memory leaked all over the place when it does so.

The Files Involved

You can see that labeled_marker.js now has the Apache License boilerplate on it. This is a very general license whose main provision is attribution. That is, you’re welcome to use or redistribute labeled_marker.js in whole or in part, just please always leave my name and copyright notice attached to it. (There’s another reason for choosing this particular license, but for that you’ll have to wait and see…)


24 Responses to “Clickable LabeledMarker”  

  1. 1 Joseph Crawford

    Hello,

    I wanted to take this time to thank you for keeping this blog up to date with more samples like this. My employer wanted me to create a google maps mashup for them and I had never before used JavaScript. They expensed the cost of your book for me and I must say this is really a great book and well worth the cost. I have learned quite a bit about JavaScript and the Google API just by picking up the book and getting my hands dirty.

    Over the last week this is what was created for my employer.
    http://www2.ereawards.com/attendee-map/

    Thanks again for all of the hard work you put into writing this book, it will be a great reference tool on my bookshelf.

  2. 2 Mike

    Joseph: That’s a neat map, thanks for sharing. I like the concept of clicking on the zoomed-out marker to see the closer view. Maps itself does something similar for the new Traffic view, but you have to click to bring up the infoWindow, and then click again to zoom to street level…

    Anyhow, I’m glad you’re enjoying the book and blog. Let us know if you have any questions or a request for a particular tutorial topic. :)

  3. 3 Ant Burnett

    Nice work Mike,

    Firebug throws a message “Element referenced by ID/NAME in the global scope. Use W3C standard document.getElementById() instead.” pointing to
    labeled_marker.js (line 84)

    I changed line 84 from “GMarker.prototype.redraw.call(this, map);” to “GMarker.prototype.redraw.call(this, document.getElementById(’map’));” and this appears to have fixed it.

    Is this a valid improvement?

  4. 4 Mike

    Ant: That’s actually a sloppy copy & paste error. The correct fix would have been to replace map with force, since we’re just passing through the arguments. However, an even better fix is to just use the apply() function instead of call(), like so:

    GMarker.prototype.redraw.apply(this, arguments);

    But thanks for pointing it out! I’ve made the relevant fixes in the source file.

  5. 5 Mike G

    First this is simply great. Been looking around for a way to add labels to the markers for a little while now. I’ve been hacking this into an APEX application and have hit a road block. First I am pretty new to this stuff so I could just be noobing it up. But maybe someone can point me in the right direction on this problem. I’ve got the js scripts added into this. I can get googlemaps to come up with the display world on it. But when it executes the createMarker function it calls the LabeledMarker function. When it does this it blows an error saying LabeledMarker is undefined. However, it being included in the script so it should be there somewhere… Any suggestion on things to look at as to why this would be occuring? I have not modified js scripts except to remove the stuff about the markers arrary and manager since, I am pulling this info from a database. I am populating variable that should be for all intense purposes the same as markers array to pass into the createMarker function. Thanks for your help. Let me know what other info I can provide.

  6. 6 Mike G

    Never mind… I noobed it. Silly typo got me….

  7. 7 Anil

    this.Me is not a function
    http://maps.google.com/mapfiles/maps2.78.api.js
    Line 1058

    I see that message when I try to implement LabeledMarkers with marker clusters and different zooms. (can you tell I am new…) I’ve been playing with google’s examples:

    function setupOfficeMarkers()
    {
    mgr = new GMarkerManager(map);
    for (var i in officeLayer) {
    var layer = officeLayer[i];
    var markers = [];
    for (var j in layer[”places”]) {
    var place = layer[”places”][j];
    var posn = new GLatLng(place[”posn”][0], place[”posn”][1]);
    opts = {
    title: place[”name”],
    icon: getIcon(place[”name”]), “clickable”: true,
    “labelText”: “Test”,
    “labelOffset”: new GSize(-4, 8)
    };
    var marker = new LabeledMarker(posn, opts);
    GEvent.addListener(marker, “click”, function() {
    marker.openInfoWindowHtml(”Hello world.”);
    });
    markers.push(marker);
    }
    mgr.addMarkers(markers, layer[”zoom”][0], layer[”zoom”][1]);
    }
    mgr.refresh();
    }

    What am I missing?

  8. 8 WolfG

    The LabeledMarker-Calss is a very nice introduction into “advanced” GoogleMaps (for occasional JS-programmers as me). Not having fully caught the basics of the OO- and prototype-features in JS I got a little problem when trying to apply the LabeleMarkers-functionality in the context of

    GEvent.addListener(map, “click”, function( overlay, latlng) { …

    The (!overlay)-test always returns TRUE (in IE 6 and 7 at least), when clicking in the TEXT-DIV-Area of the labeled Marker.
    So in this cases the MapClick- and MarkerClick - responses overlay.

    Does anyone have an idea how to avoid this, because all trials with different G_MAP panes or z-indice for the texts led to nowhere.

  9. 9 DarenJ

    Thanks for the wonderful tutorial. I am trying to figure out what part of the code controls the markers disappearing as you zoom out. I would like to use these labeled markers for a more broad map …say zoom 3 or 4. Any clues?

  10. 10 Mike

    DarenJ: That’s the MarkerManager at work. There’s some good examples in the official documentation.

  11. 11 Nathan Lambert

    Mike, I can only add my thanks to the others for your great work on this.

    A quick question: you say the labeled markers aren’t draggable — can they be re-positioned with the normal setPoint() function?

  12. 12 Mike

    Nathan: There are various ways that draggability could be added, but all of them bloat out the source of this. For those who need them draggable, I’m sure it can be added without too much additional effort.

  13. 13 Tatlar

    Hi Mike, thanks for a great tutorial - I am developing a similar application for one of my sites using your source. Development page is http://eqinfo.ucsd.edu/~rnewman/howtos/maps/google_multiple_nodes.php

    I have a question - is there a way I can dynamically turn on and off the display of labeled markers in the LabeledMarker script? In my script that I wrote, I have ‘nodes’ on a marker, that are dynamically plotted with a GEvent ‘click’ handler. I want to dynamically ‘unload’ them too with a click event. Any pointers would be helpful.

    Again - huge thanks for providing a clear and concise tutorial.

  14. 14 Charles

    This is a create little bit of code, and i’m using and extending it on a new site, thanks!

    One big problem I have is that when a marker is clicked on I need to raise the zIndex so that it shows above any overlapping markers. Any clues/ideas on how this might be achieved? Is it something that the underlying maps code would need to support?

    I keep finding references to undocumented functions one being marker.setZIndex, but this doesn’t seem to work for me.

    Thanks again..

  15. 15 Karstyn

    I had this working on both Mac and Win, and still works on Win, but on a system that was just upgraded to OSX Leopard the maps will not load. Have you heard of any problems with Safari 3?

  16. 16 Mike Purvis

    Karstyn: I haven’t tried it myself, but I have a report that it works fine.
    Please be sure to use the official version here:

    http://code.google.com/p/gmaps-utility-library/

    Charles: There isn’t really any shortcut for how to do this… your safest bet
    is probably to access the marker’s icon url directly, and add a second
    copy of it, with the z-index set appropriately. So basically, do:

    var icon_url = my_marker.getIcon().image;

    And then add a new element to the G_MAP_MARKER_PANE, with the
    css zIndex set to some extremely high value. Find the position using
    this function:

    var position = map.getProjection().fromLatLngToPixel(my_marker.getLatLng(),
    map.getZoom());

  17. 17 Charles

    Hi Mike,

    Just a quick follow up on my question about change the z-index of a live marker. In the end I had to create a array of live markers and manually remove and re-add a marker every time i needed to change its z-index. It works quite well, and after many months the site is pretty much done!

    Check it out at http://www.thebandbdirectory.co.uk

    Thanks for the help!

    Charles.

  18. 18 Dave

    Thanks for the code. Not immediately sure why you disable dragging. Besides modifying the constructor, the only other change I had to make was to change one line in the redraw function to:
    var p = this.map.fromLatLngToDivPixel(this.getPoint());
    (latlng changed to getPoint())

  19. 19 Swapnil

    Hello

    This is really a very helpful article I am finding exactly what you have mentioned.
    Only Thing I want to ask you that I want to show User the Points on Map which will guide him the points visited Now I just want to give that particular points naming that is sequence like 1,2,3,4,5………….. as per they have been visited.

    Using this article I think I can do it But the problem is The Opts You have mentioned one time that Opts can I reinitailize in array depending upon the Number of positions I am having I hope you have got my point .

    Please reply if you can as its been urgent to me.

    thanks & takecare

    Swapnil S

  20. 20 Peter

    Hi,

    Question about the map_data.php file. I’m having a tough time trying to create that data structure to feed into the api from a database because I have no idea what this is in javascript:

    var markers = [
    {
    ‘abbr’: ‘LES’,
    ‘name’: ‘Lower East Side’,
    ‘latitude’:40.714737,
    ‘longitude’:-73.986912,
    ‘wp’:'http://en.wikipedia.org/wiki/Lower_East_Side%2C_Manhattan’
    },
    {
    ‘abbr’: ‘EV’,
    ‘name’: ‘East Village’,
    ……etc.

    Is that a short hand method for creating an area, or an object? I have successfully retrieved my points data from php/mysql using GXmlHttp (and I’ve iterated through the structure and it’s all there). My question is how do I recreate the above structure in a for loop in javascript once the data has been retrieved. I tried creating an array and pushing elements on it but I don’t think that is the same data structure as above. Any ideas?

  21. 21 Mike Purvis

    Peter: The square brackets create an array, the braces create an object.
    Equivalent code would be like so:

    var markers = new Array();

    var myMarker = new Object();
    myMarker.abbr = “LES”;
    myMarker.name = “Lower East Side”;
    // etc.

    marker.push(myMarker);

  22. 22 Peter

    Hi Mike,

    Thanks for the speedy response! That worked, know I’m able to load my markers using GXmlHttp. I had one follow up question. For awhile I thought it might be a bug with my code, but then I tried confirming it with the Manhattan sample you have linked to the article, and it appears to be true. GIcons do not appear at a zoom level less than 11 (in other words if I set the start zoom level of the map at 10 or less, the icons do not appear). If I zoom into the map, as soon as I hit a zoom level of 11, they appear. Is there a reason for this. My data points are fairly spread out geographically and there is no way to see them all unless you zoom it.

    Other than that, everything seems to work perfectly.

  23. 23 Peter

    Hi again,

    please ignore the last question (I just read a response in this thread that answers it).

    thanks.

  24. 24 Ruud

    Hello,

    I’m using also this script for an school case which you can see here: http://webkrant.redje.net

    Sorry for this noob question but how do i set the map view on hybrid mode standard? I’ll tried some methods but no result at all…

    tnx in advance!

Leave a Reply