In this tutorial, you're going to learn to use the Google Maps API to display a map on your page, add markers to it, and manage info windows. The Google Maps API is very large and detailed, and there's no way to reasonably present it in a single tutorial, but I've included some examples below that will get you started with basic map creation quickly, and point you in the right direction to learn more in online documentation.
So how do you? That part's pretty simple: You link to the API in a <script> tag, and then you have access to the entire Google Maps library. So before you continue this tutorial, include the following in your practice document's head:
From here, you can now begin creating some abstract 'map', 'marker' and 'infowindow' objects that Google will recognize and handle for you. Let's start by simply displaying a map. To do so, we're going to create a map object and provide it with a few pieces of info: the element on our page that we want Google to use as the container, a set of lat/lng coordinates (by creating and passing a Google LatLng object), and an initial zoom setting. This might all be initiated when the page is loaded using window.onload, however as is typically the case in these tutorials, the code in the lessons below will run when you click the button below the code block.
Let's jump right into some code to display a Google map, and then we'll see what's going on in the code in the explanations that follow:
Note: we're using JSON (we've seen this before!) to build a set of options to hand the Map constructor. This means that the 'mapOptions' object that is created can be passed as a single argument into a constructor that will build an instance of Google's 'Map' class.
Let's take a look at the code. First of all, as I mentioned earlier, we needed to get together some information to provide to Google. Google can handle a lot of the effort of building a map (providing graphics, calculation, and interactivity preparation like dragging, zooming, widgets and fitting it properly into our container), but we have to be ready to tell it what zoom to start at, what lat/lng to center on, and what container to place it in on our page. The map constructor will eventually take two arguments: first the container, and second a set of options with the other information. The container is pretty straightforward: we simply grab a reference to it using document.getElementById as usual. Let's take a look at how the other options are set before proceeding to call up the actual map.
The first option - the zoom - is easy. You'll get used to the various zoom options as you work with Google, but the basic concept is that higher numbers zoom you in closer. Here we've set it to 12, so we'll see a view of a portion of a city. The LatLng object that will serve as the indicator of the center of the map is also fairly simple to construct. Google just wants a number for the latitude and longitude, as each of the two arguments of the constructor, respectively. In this case, I've pointed it at Greensboro, North Carolina. Take a moment to note how the constructor is structured: 'google.maps.LatLng(...)'. All references to the Google Maps library will begin with 'google.maps' and continue with a more specific reference to a subset of the library... perhaps an object class, or possible a library of values, as you'll see in the following paragraph when we choose the type of map to display.
The final option is the 'mapTypeId', which requires a numerical reference to a type of map: road, satellite, topographical, etc. It seems a little silly to use an actual number here, since... well... how would you remember these numbers? And what if they changed later? Instead, Google provides some constant variable values that you can use instead. In this case, we reference the 'MapTypeId' library: google.maps.MapTypeId.ROADMAP.
Once we've compiled our two arguments - container and options - we can simply create a new instance of a Google Map that uses them, which is seen in the final line. Note that the "var map = " part was actually optional in this first example, however since we'll want to do things to this map in future sections of our code, it's wise to go ahead and create a variable reference to it in this way. This will make more sense when we start placing markers on the map and popping up information windows.
Now that we have a map, we have something that we can put markers on. The Google 'Marker' class is designed for exactly that. We'll be able to again collect a few pieces of info (this time the LatLng to place it at, the map to place it on, and a title to give it when hovered over) and then pass these into a constructor that will do the grunt work of displaying a marker image on the map. Let's place two of them on the map and then see what's going on:
There are three blocks here. In the first block, we've simply duplicated what we saw in our first example to produce a map (this time called 'map2'). The second and third blocks are identical (each creating a marker on the map), so let's just look at how we created the first one. First of all, just like our map, the marker we wanted to create expected a set of options. This time, we didn't need to include the map container, because Google already knows what container out map is in, so we only had one argument to send to the constructor: the options object. However we DID have to mention WHICH map we wanted to place it on when building the options. Then we needed to specify where on the map it should go. Again, we constructed a LatLng object (using the coordinates of these particular coffee shops in Greensboro) that we could use as the 'position' value in our options object. Finally, we provided an optional title value, so that hovering over the pin will result in a pop-up of that text as a clue to what the marker is pointing at. Once we have those options compiled into an object, we can simply call up a google.maps.Marker constructor and pass it in, as seen in the last line of the code above.
This time I've only added one marker, and I've opened an info window immediately. Let's take a look at how the immediate info window was created, and then we'll take a look at an example that uses multiple pins and listens for a click before opening up the correct info window.
To begin creating the window, I've again created an options object (infoWindowOptions), and this time it's pretty simple: I just need to pass in a single property called 'content' that contains the HTML that will be displayed when the window opens. In this case, I'm including a bolded title (available from the object, remember?) and the 'html' property that I added myself. Once I've created these options, I call up a new instance of a google.maps.InfoWindow just like I created a Map and Marker before. Once the info window exists as an object instance, it immediately has access to several methods, including setPosition(). Since I want the window to pop up at the marker's position, I pass in marker3a.position as my position argument. The window is now ready to be displayed, however Google does not show these by default (since normally they pop up when you click a pin or otherwise interact with the page in some way). Instead, the window is simply held as an abstract concept until it is opened on a particular map using the open() method and passing in the reference to the map on which it should appear, which is what we do in the final line of that block of code.
Although the Google Maps API already provides a lot of objects and methods for displaying and interacting with maps, it is often the case that we want to bundle certain tasks into our own custom functions. In the example below, I have tweaked the code from above to use a custom_createMarker() function that simply takes a set of marker options (created as usual, but possible with a few more custom properties). By doing this, we can also systematically build an info window pop-up into a click listener on each marker I create. Let's take a look:
Let's take a look at how we've added an event listener here. We're used to adding event listeners directly to actual elements in the DOM of our HTML page, because things like "onclick" and "onload" are built into things like links and our document body. But what's going on here? A Google Map Marker is an abstract object that represents the idea of a location on the map and the pin that displays it, so how are we going to add a click event listener to that? Well it turns out that (fortunately), Google Maps already knows how to add its own custom event listeners to any of the overlays that you create on your map, including markers and maps, as we'll see here in a moment. To do this, we invoke the google.maps.event.addListener() function, which takes three arguments: (1) the object to add the listener to (in our case, each pin), the event to listen for (in our case, "click"), and the function call once the event has taken place (the pin has been clicked). Handily, the function that we include will automatically have access to a reference to the marker that was clicked in the form of the infamous "this" variable. So now... remember how earlier we created an instance of a Google InfoWindow on each marker separately using variables like 'marker3a' and 'marker3b'? Now we can simply do this using the variable 'this' instead. What this means is that WHATEVER pin we click, that will be the pin that we open up an info window for. And, in turn, the information that pops up will be THAT pin's 'title' and 'html' content. So by wrapping this set of lines in an event listener as we have done, we have successfully implemented an InfoWindow-in-waiting that will pop up whenever the pin is clicked.
You may notice that what we've done so far has set us up to open an independent InfoWindow for each pin clicked. It may be the case that you want to open a single InfoWindow at any time on your map, closing any other window that you may have open when you do. If this is the case, here's a clever strategy for using one common InfoWindow instead of several: Simply attach the InfoWindow reference to your overall map (here, 'map') instead of attaching it to each marker that you create (each 'this' wrapped in a listener), and opening it anywhere on the map will automatically remove it from wherever it was before. The same 'setContent()' function used in the previous example will simply overwrite whatever was there, and the window will disappear and reappear on whichever pin you clicked.
So far we've explicitly created each marker on the map that we want to see, however it is not unlikely that you'll actually want to pull your map markers from a dynamically delivered set of location information using Ajax. You can revisit the Ajax Tutorial for a refresher on retrieving data from another page on your site, however for the example below, I'll simply start with a string of JSON, convert it using JSON.parse(), and then proceed to loop through that data to create pins using the same function as above.
Take note: I've decided that passing the map reference in my custom_createMarker() function was kind of redundant, since that information is available within the options anyway. So instead, I've simple changed the references inside of the event listener wrapper from 'map' to 'options.map'. This is nice and neat, since now I can just pass in the same options I would have passed in to create a normal marker.
We've already seen a quick example of adding an event listener to a marker. There are several useful ways you can implement event listeners to get information and/or perform an action when a user interacts with the map layer as well, including when they drag it, click it, double-click it, etc. Below I'm going to introduce three of them, having each of them do something different, and then I'll discuss how the function called for each was structured. Let's start with the code from our last example, and add a few more blocks after creating the map:
You may notice that double-clicking actually causes TWO pop-ups. The first pop-up is the one we called ("Clicked on..."). The second one is the result of zooming (hence "zoomed to level...") because Google Maps already listens for a double-click and zooms when you do it. There is a way to disable this: in your map options, including a property called "disableDoubleClickZoom" and set it to true. (It defaults to false.) I've commented that line out above, but try it yourself and see what happens.