GEOG 863
GIS Mashups for Geospatial Professionals

Handling Clicks on a Marker

PrintPrint

A disappointing aspect of the map from Lesson 3 was that clicking on the marker didn't do anything. The behavior that you're probably used to seeing when a marker is clicked is that a callout window opens with some information about that marker. To add this kind of behavior, we need to add an event "listener" for our marker. This listener will enable us to execute a function whenever certain events are triggered with respect to the marker (e.g., when it is clicked, when it is double-clicked, or when the mouse moves over it). The full list of events supported by markers can be found by looking up the Marker class in the API Reference. We will add a handler for the click event that will open an info window and display text that we've encoded as HTML.

Like several other classes we've encountered so far, an object of the InfoWindow class can be created by passing an "Options" object to its class constructor. So we'll start by defining an InfoWindowOptions object using object literal notation.

  1. Add the following code (in which the long URL has been split across two lines for readability) to the end of the initMap() function:

    var infoWindowOpts = {
      content: '<a href="https://www.youtube.com/watch?v=aLSFcF8SOiw">' + 
        'My Hometown</a>'
    };
    
    var infoWindow = new google.maps.InfoWindow(infoWindowOpts);

    The first statement in this block of code (which spans four lines) creates an object literal and sets its content property equal to a string of HTML that we want to display in the info window - a link to a website. Pay close attention to the two pairs of quotes used in this statement. The first pair is a set of single quotes used to enclose the entire string that is to be assigned to the content property. The second pair is a set of double quotes used to enclose the URL that's being assigned to the href attribute of the anchor tag. It would also be acceptable to use double quotes around the full string and single quotes around the URL. The important thing is that one kind of quotes be used to set the JavaScript content property and the other kind be used to set the HTML href attribute.

    The second statement creates an InfoWindow object based on the content setting defined in infoWindowOpts. We're now ready to create the event listener.

  2. Add the following code where you left off:

    google.maps.event.addListener(marker, 'click', function() {
      infoWindow.open(map, marker);
    });

    This code calls the addListener() method in the event namespace, passing it the object whose events we want to listen for (marker), the event we want to listen for (click), and a function to execute when that event is triggered.

    Even if you're an experienced programmer, the code used for the third argument may have you scratching your head a bit. We could have supplied the name of a function that was defined elsewhere in the document, but instead we're using something called an anonymous function. Anonymous (or inline) functions have a number of uses in JavaScript that are beyond the scope of this class, so we won't discuss them in depth. For our purposes, you should simply understand that they are frequently used to define the code to be executed as part of an event listener. Note that the function is defined in the same way as a normal function, with the exception that it is left unnamed.

    In this case the anonymous function is just one line in length, calling on the open() method of the InfoWindow. The open() method's first parameter specifies the Map to display the InfoWindow on, while the second specifies an object to anchor the InfoWindow to (typically a Marker, Polyline or Polygon).

  3. Save the file and reload it in your browser. Clicking on the marker should now open an info window that contains a link to a website. Note that we could have inserted any amount of HTML text into the info window.

    Now, let's say we want to add a second marker with its own associated info window text. We could repeat the same statements (beginning with the creation of the latlng variable and ending with the call to the addListener method). I've added such code to an example page so that you can see why an approach like this doesn't work:

    Note

    Do not enter this code into your file. This is the wrong way to add a second marker. We'll see a better way to do this momentarily.

    var latlng = new google.maps.LatLng(40.812, -77.8565);
    
    var markerOpts = { 
      position: latlng, 
      map: map 
    };
    
    var marker = new google.maps.Marker(markerOpts);
    
    var infoWindowOpts = { 
      content: '<a href="http://www.gopsusports.com/facilities/' +
        'beaver-stadium.html">Beaver Stadium</a>' 
    };
    
    var infoWindow = new google.maps.InfoWindow(infoWindowOpts);
    
    google.maps.event.addListener(marker, 'click', function() {
      infoWindow.open(map, marker);
    });
    
  4. View my bad marker example page and click on the markers to see why there is a problem with adding a second marker this way. If you click on the northern marker, an info window opens with the correct text. However, if you click on the southern marker, the info window associated with the northern marker opens again.

    Note that this piece of code re-uses a number of variables that I had defined earlier in the script. The problem with re-using these variables in this way is that when the click event for either marker is being triggered, the variables are holding the values last assigned to them, rather than the values that were originally assigned when the event handlers were set up.

    The simplest solution to this problem is to use different variable names for each marker (e.g., infoWindowOpts2, infoWindow2). However, if you're adding more than two or three markers, you'll find that such a solution is inefficient. A better solution is to create a function you can call on any time you want to create a new marker with an associated info window. That is the approach we'll take.

  5. Add the following function to your lesson4.html file, just prior to the initMap() function (after the initMap() function would be fine also):

    function createMarker(point,info,map) {
      var markerOpts = {
        position: point,
        map: map
      };
    
      var marker = new google.maps.Marker(markerOpts);
    
      var infoWindowOpts = { content: info };
      var infoWindow = new google.maps.InfoWindow(infoWindowOpts);
    
      google.maps.event.addListener(marker, 'click', function() {
        infoWindow.open(map,marker); 
      });
    }

    This function accepts a LatLng object for its first parameter (stored locally in the function in the point variable), a string for its second parameter (stored in the info variable) and a Map object for its third parameter. A marker is created from the point and a handler is set up for the marker's click event that displays an info window containing the text passed into the info variable.  The marker is added to the Map object passed into the map variable.  (It might help to keep in mind that pages aren't restricted to having just a single map.)

    In the next few steps, we'll modify the initMap() script so that it creates the marker using the new function.

    First, a word about the map variable.  Going back to the Google Hello World example, you've been working with a map variable defined outside your initMap() function.  This gives the variable a global scope, meaning it can be used in any script or function found in the document.  Thus, in this new createMarker() function we just added, we could easily utilize the global map variable to specify which map the marker should be added to.  If we were going that route, it would not make sense to define a map parameter in the createMarker() function definition.  However, defining the function with a map parameter makes it a bit more flexible because it won't be locked into adding markers to a single map.  When going this route, it doesn't make sense to use a global variable for the map.

  6. Remove the var map statement found outside your initMap() and createMarker() functions.

  7. Back in the initMap() function, insert the var keyword at the beginning of the line that defines the map variable.  This gives it a scope local to the initMap() function.
  8. Remove the var infoWindowOpts, var infoWindow and addListener() statements.
  9. Modify the var marker statement to read:

    createMarker(latlng, '<a href="https://www.youtube.com/watch?v=aLSFcF8SOiw">' + 
      'My Hometown</a>', map);
  10. Next, add code for a second marker. You can simply modify the lat/long coordinates you used for your first marker slightly if you can't think of a good place to put the second one. You may need to change the initial zoom level of the map to see both markers. Re-use the same latlng variable that you used for the first marker. After adding the code, your page's source should look something like the source of this good marker example page.
  11. Test your page to confirm that it behaves as expected. This version of the code doesn't suffer from the error we encountered above - even though it also re-uses the same variable names - because embedding the anonymous function inside the createMarker() function creates something referred to as a function closure. Each time the createMarker() function is called, new copies of its local variables are created and the values held by the variables in previous calls to the function are retained separately. Thus, each Marker object created by the createMarker() function carries with it its own properties. A complete understanding of how function closures work is beyond the scope of this course, so don't worry if you're confused. You won't be asked about them on the quiz. :-)

Now that you've seen how to display an info window in response to a click on a marker, let's see how you can plot a marker using a custom icon.