As many users browse websites on their mobile phones, you might need the ability to guide the user from their current location to your location. This is easily accomplished using the browser's built-in navigator.geolocation object and Google Maps. The geolocation object provides the latitude and longitude of the user's phone or desktop. You can embed a Google map on your Web page and show the user their location based on that latitude and longitude. Additional API calls to Google's mapping API can give the user step-by-step directions to your location. This article shows you how to get started using these two powerful APIs.

Create HTML to Display Location Information

To begin, build a simple HTML page to display the various properties of the geolocation object. Feel free to use your tool(s) of choice to build an HTML page named index.html and a Web project. You can use Node.js/Express, MVC, Web Forms, etc. All you need is a Web server to run the HTML page and a CDN reference to jQuery. After creating your Web project and the index.html page, open the index.html file and add the code shown in Listing 1 into this file.

Listing 1: Create an HTML page to display the geolocation coordinate information.

<!DOCTYPE html>
<html>
<head>  
  <meta charset="utf-8" />
  <title>Get Latitude/Longitude</title>
  <link href="styles/site.css" rel="stylesheet" />
</head>
<body>  
  <header>
    <h1>Get Latitude/Longitude</h1>  
  </header>
  <main>
    <p>Timestamp: <span id="timestamp"></span></p>
    <p>Latitude: <span id="lat"></span></p>
    <p>Longitude: <span id="long"></span></p>
    <p>Lat/Long Accuracy (meters): <span id="accuracy"></span></p>
    <p>Altitude (meters above sea level): <span id="altitude"></span></p>
    <p>Altitude Accuracy (meters): <span id="altitudeaccuracy"></span></p>
    <p>Heading (Degress from true north): <span id="heading"></span></p>
    <p>Speed (meters/second): <span id="speed"></span></p>
    <div id="errorArea" class="alert d-none"> </div>
  </main>
  <script src="https://code.jquery.com/jquery-3.5.1.min.js"; integrity="REMOVED FOR BREVITY" crossorigin="anonymous"> </script>
  <script>
    'use strict';
    $(document).ready(function () {  }
    function displayPosition(pos) {  }
    function handleError(error) {  }
    function displayError(msg) {  }  
  </script>
</body>
</html>

Styles

The index.html file references a stylesheet named site.css. In this file is where you place some CSS rules to set some margins, padding, and some color. Add a file named site.css in a styles folder (add this folder if needed) and place the code shown below into this file.

header, main { padding-left: 2em; }
.alert {  margin-top: 1em;  margin-bottom: 1em;  padding: 1em;  background-color: red;  color: white;}
.d-none { display: none; }

Get Latitude and Longitude

When using the geolocation object, the first thing you should do is check to ensure that the user's browser supports the object. After your page loads, check to see if something other than a null or undefined is returned when you check the navigator.geolocation property. If a geolocation object is returned, you can call the getCurrentPosition() method on that object and pass in the name of a function to call once the current location has been determined. If a geolocation object isn't returned, call a function to display an error message. Add the code shown below within the $(document).ready() function.

$(document).ready(function () {  
    if (navigator.geolocation) {    
        navigator.geolocation.getCurrentPosition(displayPosition);  
    } 
    else {
        displayError("Please update your browser to use Geolocation.");  
    }
});

Display Latitude and Longitude

The getCurrentPosition() method asks the user's permission to retrieve their location. The browser prompts the user to answer with an OK or Yes if they will allow the getCurrentPosition() method to retrieve their current location. If the user answers in the affirmative, the user's location is retrieved, and the data is placed into a Coordinates object. This Coordinates object is passed to the callback function you passed into the getCurrentPosition() method. Within the displayPosition() function is where you extract the latitude, longitude, and other properties. Add the following code within the displayPosition() function.

function displayPosition(pos) {  
    let coords = pos.coords;    
    $("#timestamp").text(new Date(pos.timestamp));
    $("#lat").text(coords.latitude);
    $("#long").text(coords.longitude);  
    $("#accuracy").text(coords.accuracy);  
    $("#altitude").text(coords.altitude ?? "n/a");  
    $("#altitudeaccuracy").text(coords.altitudeAccuracy ?? "n/a"); 
    $("#heading").text(coords.heading ?? "n/a");  
    $("#speed").text(coords.speed ?? "n/a");
}

Try It Out

Save the changes to all your files and run your Web project. Your browser prompts you that this Web page is asking to know your location. There will be two options: to allow this page to access the geolocation coordinates or decline access. For now, go ahead and allow access to see your latitude, longitude, and other properties. You should see data appear like the data shown in Figure 1.

Figure 1: Display Latitude, Longitude, and Accuracy
Figure 1: Display Latitude, Longitude, and Accuracy

Add Error Handling

You should not assume that the geolocation object will always work. There are a few things that can go wrong: the user may not allow access to the geolocation object, the call to getCurrentPosition() may timeout, the current user's location may not be able to be determined, and other errors may occur. In case an error does occur, make sure to set up an error message to display on the page.

In the index.html page you created, there is a <div id="errorArea"> where you place any error messages. This <div> has a style of “alert,” which displays error messages with a red background and white letters. The style of “d-none” is also in the class attribute. This hides the <div> until you're ready to display an error message. Add the following code in the displayError() function.

function displayError(msg) {  
    $("#errorArea").removeClass("d-none");  
    $("#errorArea").html(msg);
}

Handle Location Errors

The second parameter you may pass to the getCurrentPosition() method is a callback function to handle any errors that may occur in that method. Add the code shown in Listing 2 to the handleError() function stub you created earlier.

Listing 2: Handle location errors by displaying a specific error message.

function handleError(error) {  
    let msg = "";
    console.log("error.message = " + error.message);  
    
    switch (error.code) {
        case error.PERMISSION_DENIED:
            msg = "User does not want to display location.";
            break;
        case error.POSITION_UNAVAILABLE:
            msg = "Can't determine user's location.";
            break;
        case error.TIMEOUT:
            msg = "The request for geolocation info timed out.";
            break;
        case error.UNKNOWN_ERROR:
            msg = "An unknown error occurred.";
            break;  
    }
    displayError(msg);
}

Pass in the name of the handleError() function as the second parameter in the call to the getCurrentPosition() method.

if (navigator.geolocation) {  
    navigator.geolocation.getCurrentPosition(displayPosition, handleError);
}

Try It Out

Run the page again, but this time when the browser prompts you to use geolocation, answer in the negative. You should now be presented with a page that looks like Figure 2.

Figure 2: Create an error area to display error messages
Figure 2: Create an error area to display error messages

Geolocation Options

There's a third parameter you may pass to the getCurrentPosition() method. This third parameter is an optional PositionOptions literal object. This object contains three properties and you may set one, two, or all three of them. One thing to note: these options are only applied if you're running in HTTPS mode in your browser. The properties and their meanings are listed in Table 1.

To use these options, add a new variable named options, and set it equal to a literal object you see in the code in bold below. Pass this object as the third parameter to the getCurrentPosition() method.

if (navigator.geolocation) {  
    let options = {    
        enableHighAccuracy: true,    
        timeout: 10000,    
        maximumAge: 300000  
    };
    
    navigator.geolocation.getCurrentPosition(displayPosition, handleError, options);
}

Create a Geolocation Closure

The code shown so far is fine for this page but requires you to copy and paste the code and make significant changes if you want to use this code on multiple Web pages. To make the geolocation code more reusable, wrap it into a closure and place that closure in a JavaScript file. Create a file named geoController.js in a scripts folder (create this folder if needed) and add the code shown in Listing 3.

Listing 3: Handle location errors by displaying a specific error message.

'use strict';

let geoController = (function () {  
    // *****************************  
    // Private Variables  
    // *****************************  
    let position = null;  
    let lastMessage = null;  
    let lastError = null;  
    let successCallback = null;  
    let errorCallback = null;
    
    // Enable high accuracy, if available  
    // Timeout after 10 seconds  
    // Only cached positions where age < 5 minutes  
    let options = {    
        enableHighAccuracy: true,    
        timeout: 10000,    
        maximumAge: 300000  
    };
    
    // *****************************  
    // Private Functions  
    // *****************************  
    function getCurrentPosition(success, error, posOptions) {
        // Set callbacks    
        successCallback = success;    
        errorCallback = error;    
        if (posOptions) { options = posOptions; }
        
        // Reset private variables    
        position = null;    
        lastError = null;    
        lastMessage = null;
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(setPosition, handleError, options);
        }    
        else {
            callError("Update your browser to use Geolocation.");    
        }  
    }
    
    function setPosition(pos) {
        position = pos;
        if (successCallback) {
            successCallback(position);
        }  
    }
    
    function handleError(error) {
        lastError = error;
        switch (error.code) {
            case error.PERMISSION_DENIED:
                lastMessage = "User does not want to display their location.";
                break;      
            case error.POSITION_UNAVAILABLE:
                lastMessage = "Can't determine user's location.";
                break;
            case error.TIMEOUT:
                lastMessage = "The request for geolocation information timed out.";
                break;
            case error.UNKNOWN_ERROR:
                lastMessage = "An unknown error occurred."
                break;    
        }
        callError(lastMessage);  
    }
    
    function callError(msg) {
        lastMessage = msg;
        console.log(msg);
        if (errorCallback) {
            errorCallback(lastMessage);
        }  
    }
    
    // *****************************  
    // Public Functions  
    // *****************************  
    return {
        "getCurrentPosition": function (success, error, posOptions) {
            getCurrentPosition(success, error, posOptions);
        },
        "getPosition": function () {
            return position;
        },
        "getLastError": function () {
            return lastError;
        },
        "getLastMessage": function () {
            return lastMessage;
        },
        "getOptions": function () {
            return options;
        }  
    }
})();

The basics of this closure are created using an Immediately Invoked Function Expression, more commonly called an IIFE. This IIFE starts with the second line in the file and ends with the last line of the file. In between are some private variables, private functions, and a literal object returned from this IIFE to identify the public functions to be called from the geoController variable, as shown in Listing 4.

Listing 4: The structure of your closures should follow this format.

let geoController = (function () {  
    // Private variables here
    
    // The following are private functions  
    function getCurrentPosition(success, error, posOptions) {  }
    function setPosition(pos) {  }
    function handleError(error) {  }
    function callError(msg) {  }
    return {
        // Public functions here  
    }
})();

The only private function that's exposed as a public function is getCurrentPosition(). The rest of the functions allow you to retrieve values set in the private variables such as the position object, the last error object, the last error message, and the position option object. The getCurrentPosition() function has the same signature as the getCurrentPosition() method on the geolocation object to make it easy to understand. The two callbacks are setPosition() and handleError() within this closure. This allows you to set the private variables from these two functions before calling the success or error callbacks you pass in.

Notice that in the closure, there's no code that affects the UI. All this function does is set data. You can then call any of the public functions to retrieve the data and use that to set the UI on your specific page. Open the index.html page and change the code at the bottom of the file to look like Listing 5.

In the code in Listing 5, you see the call to the geoController.getCurrentPosition() function. Pass in two function names on this page as the callbacks to be used to set the position data into the page, and to display an error message. You now have a nice separation of concerns as far as code that gathers data and code that displays data.

Listing 5: Call the closure to set data, then make a call back to your page to affect the UI.

<script src="scripts/geoController.js" type="text/javascript"></script>
<script>  
    'use strict';
    
    $(document).ready(function () {
        geoController.getCurrentPosition(displayPosition, displayError);  
    });
    
    function displayPosition(pos) {
        let coords = pos.coords;
        $("#timestamp").text(new Date(pos.timestamp));
        $("#lat").text(coords.latitude);
        $("#long").text(coords.longitude);
        $("#accuracy").text(coords.accuracy);
        $("#altitude").text(coords.altitude ?? "n/a");
        $("#altitudeaccuracy").text(coords.altitudeAccuracy ?? "n/a");
        $("#heading").text(coords.heading ?? "n/a");
        $("#speed").text(coords.speed ?? "n/a");  
    }
    
    function displayError(msg) {
        $("#errorArea").removeClass("d-none");
        $("#errorArea").html(msg);  
    }
</script>

Pass Coordinates to Google Maps

Once you have the latitude and longitude from the geolocation object, you have a variety of options on how to display those coordinates. There are free and paid services you can use. For example, with Google Maps, Bing Maps, or Open Street Maps, you can redirect to their Web pages and pass in the coordinates. You can do this for free. Using Google Maps or Bing Maps, you also have the choice of paying for their service and having a map embedded right into your own Web page. Let's take a look at the free option first. Open the index.html page and add a button within the <main> element.

<button onclick="displayOnMap();">Display on Map</button>

Add a function within the <script> tags named displayOnMap() as shown in the code below.

function displayOnMap() {
    let coords = geoController.getPosition().coords;
    window.location.href = 'https://google.com/maps?q=${coords.latitude}, ${coords.longitude}';
}

This code retrieves the GeoLocationPosition object by calling the getPosition() method in your closure. It then extracts the coords property and puts it into a local variable named coords. Pass in the latitude and longitude on the URL line in a format that looks like the following:

https://google.com/maps?q=36.084391,-86.773550

The code in displayOnMap() sets the window.location.href property using a template string to embed the latitude and longitude retrieved from the coords variable. Once the href property is set, your browser redirects to Google Maps and displays a marker at those coordinates, as shown in Figure 3.

Figure 3: Google Maps can display any latitude and longitude through the URL.
Figure 3: Google Maps can display any latitude and longitude through the URL.

Of course, the problem with this approach is that you must leave your site and go to the Google Maps site. A better approach is to embed a Google Map within your Web page. The next part of this article covers how to accomplish this.

Embed a Google Map

Simplify the <main> element by removing most of the <p> and <span> elements except the ones used to display latitude and longitude. Add a new <div> element just above the <div id="errorArea"> and add id and class attributes to the new <div> element, as shown in the code snippet below.

<main>  
  <p>Latitude: <span id="lat"></span></p>
  <p>Longitude: <span id="long"></span></p>
  <div id="map" class="mapArea"></div>
  <div id="errorArea" class="alert d-none">  </div>
</main>

Open the styles\site.css file and add a new CSS rule for the mapArea class. Add the CSS rule shown below to set the width and height of the <div> where the map is to be embedded.

.mapArea { width: 90%;  height: 500px; }

Fix the Script Code

Remove the $(document).ready() function and replace it with a function named initialize().

function initialize() {  
    geoController.getCurrentPosition(displayPosition, displayError);
};

Locate the displayPosition() function and remove the code that set the elements you removed from the HTML. Also, make a call to a new function named addMap().

function displayPosition(pos) {  
    let coords = pos.coords;  
    $("#lat").text(coords.latitude);  
    $("#long").text(coords.longitude);
    addMap(coords);
}

Add a new function named addMap() just below the displayPosition() function. This method looks like the following:

function addMap(location) {  
    // Create a lat/lng object  
    let pos = new google.maps.LatLng(location.latitude, location.longitude);  
    
    // Create map options  let mapOptions = {
        center: pos,    
        zoom: 16,    
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };  
    
    // Create new google map  
    let map = new google.maps.Map(document.getElementById("map"), mapOptions);
}

The addMap() function builds a new LatLng object by passing in the latitude and longitude properties from the geolocation coordinates retrieved from the call to getCurrentPosition(). Next, create a map options object with the properties center, zoom and mapTypeId. Set the center property to the LatLng object so the map knows where to put the coordinates on the map. Set the zoom property to 16 so the API can show just a certain portion of the map. You might want to play with this property to get make it look the way you want. The mapTypeId property is a constant that tells the type of map to display. Valid values are roadmap, satellite, hybrid, or terrain. The last piece of code is to create a Map object by passing in the HTML object where you wish to embed the map and the map options object.

Create a Closure for Mapping

Just like you created a closure for working with geolocation, I recommend that you use a closure on each HTML page as well. Immediately after the 'use strict', add the code shown below.

<script>
'use strict';

var gmapController = (function () {
    return { "initialize": initialize }
})();

// Rest of the code here
</script>

Take all of the functions that were there before and move them within the closure you just created. By moving all of the code within the function, each of those functions becomes private within the gmapController function. The only function that needs to be made public from the closure is the initialize() function, so that's the one you add to the literal object returned from the closure.

Call the Initialize Method

Because you removed the $(document).ready() function and moved all of the code within a closure, how does the map get displayed? There's one more piece to this puzzle that you add to your page. Put the following <script> tag immediately after the ending </script> tag where your code is located.

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE&;callback=gmapController.initialize" type="text/javascript">
</script >

In the above code, you need to add a developer API key, which is covered in the next section of this article. The “callback=gmapController.initialize” is passed into the Google JavaScript and tells the API that after it has loaded all its code, it should call the initialize() function within the closure on this page. This's how you get your map embedded on your page.

Get a Google API Key

Before you can embed a Google map, you must sign up with Google to get an API key at https://tinyurl.com/y5reu2l2. Be sure to read the instructions on this page carefully, as you should turn on various API services based on what you wish to use. In addition, you should also restrict the usage of your API key to only a few applications. You might also want to set daily or monthly limits on the amount that can be spent to ensure that you don't blow out your budget.

Although it doesn't cost anything to obtain a Google API key, you must enter a credit card. At the time of this writing, you get $200 per month of their various mapping APIs free. This amount should be more than enough to get your maps working within your website.

Try It Out

Once you create your Google API key, substitute your key for the YOUR_KEY_HERE string included in the <script> tag from the last section. Run your page and if you've done everything correctly, you should see a map appear with your current location in the center of the map. If you don't see the map appear, check the browser's console window for any error messages that could guide you on how to fix the error.

Add a Marker on a Location

A popular item you want on a map is a large marker (see Figure 4) to denote the location you're trying to point out to the user. Google Maps makes it easy to add a marker on your map.

Figure 4: A marker helps identify your location on the map
Figure 4: A marker helps identify your location on the map

Instead of using your location to position the embedded Google Map, let's set up a fictitious company named “Paul's Training Company” located in Brentwood, TN, USA. Add two private variables within the closure.

let map = null;
let location = {
    "latitude": 36.036020,  
    "longitude": -86.787600,  
    "title": "Paul's Training Company",  
    "marker": null
};

The map property is going to hold a reference to the Google Map object. The location property is a literal object with the latitude, longitude, and the title of the location. The marker property is currently set to null but is going to be set by code you write very soon.

Because you're not using the call to the geolocation object to get the coordinates, just call the addMap() function passing in the location variable. Modify the initialize() function in your closure to look like the following code snippet.

function initialize() {  
    // Draw the map  
    addMap(location);
    
    // Add marker for location  
    addMarker(location);
}

Add an addMarker() function within the closure to create and add the marker at the coordinates in your location variable.

function addMarker(location) {      
    // Create a new marker  
    location.marker = new google.maps.Marker( {
        position: new google.maps.LatLng(location.latitude, location.longitude),
        map: map,
        title: location.title  
    });
    
    // Add marker to the map  
    location.marker.setMap(map);
}

Try It Out

Save all your changes and run the page again. You should now see a marker on the embedded map. If you hover over the marker, you should see the value from the title property in your location property.

Add Multiple Locations

You may add as many markers as you want on your Google Map. You just need separate coordinates for each marker. Now you see the reason for creating a location literal object; it contains a property for each piece of data for each marker you wish to place on the map. Remove the private variable location you created earlier and replace it with an array named locations.

let locations = [];

Create a new function within the closure named createLocations() in which you push as many different literal objects into the locations array as you want to display on the map.

function createLocations() {  
    locations.push({ "latitude": 36.036020, "longitude": -86.787600, "title": "Paul's Training Company", "marker": null });
    locations.push({ "latitude": 36.0339689, "longitude": -86.8058154, "title": "Mere Bulles", "marker": null });
}

You need to call this new function from the initialize() function, then loop through each item in the locations array and pass each item to the addMarker() method. Modify the initialize() function to look like the following.

function initialize() {  
    // Create array of map definitions  
    createLocations();
    
    // Draw the map
    addMap(locations[0]);
    
    // Draw the locations  
    for (let index = 0; index < locations.length; index++) {
        // Add marker for location
        addMarker(locations[index]);
    }
}

Try It Out

Save your changes and try out this new code. You may have to scroll to the left to see the other marker depending on the size of your browser window. In the next section, you learn how to ensure the user can see all of your locations without having to scroll.

Center All Locations on Map

You shouldn't force your user to scroll to see all the locations you placed on your map. It would be nice if you could figure out the farthest latitude and longitudes to the north/south and east/west and somehow tell the map to automatically include these farthest points. Google Maps provides this in a LatLngBounds class. Create a LatLngBounds class as the first line of code in the initialize() function.

function initialize() {  
    // Create a lat/lng boundary  
    let bounds = new google.maps.LatLngBounds();
    
    // Rest of the code
}

A LatLngBounds class has an extend() method to which you pass in a LatLng class. The LatLng class contains two properties lat and lng. Your addMarker() function creates a LatLng object from the latitude and longitude in your map definition object. Call the extend() method after calling the addMarker() function within the loop in the initialize() function.

for (let index = 0; index < locations.length; index++) {  
    // Add marker for location  
    addMarker(locations[index]);
    
    // Extend boundaries to encompass marker  
    bounds.extend(locations[index].marker.position);
}

After drawing all of the locations in the loop, call the fitBounds() method on the map passing in the LatLngBounds object. Add the following code immediately after the loop in the initialize() function.

// Fit the map to boundaries      
if (locations.length > 1) {  
    map.fitBounds(bounds);
}

Try It Out

Save your changes and run the page again. Now, the map should resize to the appropriate width to show you both locations you added.

Directions Between Two Points

Besides just showing markers on your map, you might want to provide your user with the ability to drive to your location from where they are. Google Maps provides the DirectionsService and the DirectionsRenderer classes to connect two points with a blue line, as shown in Figure 5.

Figure 5: Google Maps can add a line showing how to travel from point A to point B.
Figure 5: Google Maps can add a line showing how to travel from point A to point B.

Add a new function to your closure named addDirections(). This function creates an instance of a google.maps.DirectionsService class and an instance of google.maps.DirectionsRenderer class. Call the route() method on the DirectionsService class, passing in a literal object with the origin, destination, and travelMode properties set to the appropriate values. The second parameter to the route() method is a callback function in which you check the status to see if the directions were able to be rendered. If they are, call the setDirections() method passing in the response passed to the callback. Call the setMap() method and pass in the map object and Google Maps will draw the blue line as shown in Figure 5.

Call the new addDirections() function at the end of the initialize() function. Create two new LatLng objects from the first two entries in your locations array. For this article, you're using latitude and longitude to draw the directions between the two locations. However, the route() method on the DirectionsService class also accepts two addresses.

// Add directions between the two points
addDirections(new google.maps.LatLng(
    locations[0].latitude, locations[0].longitude), 
    new google.maps.LatLng(locations[1].latitude, locations[1].longitude)
);

Listing 6: Use the DirectionsService and DirectionsRenderer classes to get directions between two points.

function addDirections(from, to) {  
    let service = new google.maps.DirectionsService();  
    let renderer = new google.maps.DirectionsRenderer();
    
    // Set route of how to travel from point A to B  
    service.route({
        origin: from,
        destination: to,
        travelMode: 'DRIVING'
    },    
    function (response, status) {
        if (status === 'OK') {
            renderer.setDirections(response);
            
            // Render directions on the map
            renderer.setMap(map);
        } 
        else {
            console.log('Directions request failed due to ' + status);
        }
    });
}

Try It Out

Save your changes to this page and redraw the Web page. You should now see a blue line connecting the first marker with the second marker. If you don't see a blue line, check the console window for any error messages.

Directions with Text

In addition to just showing a blue line, Google Maps can also display step-by-step directions as text on your Web page, as shown in Figure 6.

Figure 6: Google Maps can add directions in text format between two points.
Figure 6: Google Maps can add directions in text format between two points.

Determine where on your Web page you wish to have Google Maps draw the text directions and add a <div> in that location, as shown in the code snippet below.

<div id="directionsArea"> </div>

In the addDirections() function, add the code shown below after the call to the setMap() method. The code in bold calls the setPanel() method on the DirectionsRenderer class and you pass in a reference to the HTML <div> element within which you want Google Maps to render the text directions.

if (status === 'OK') {  
    renderer.setDirections(response);  
    
    // Render directions on the map  
    renderer.setMap(map);    
    
    // Call setPanel() to display text directions  
    renderer.setPanel(document.getElementById("directionsArea"));
}

Try It Out

Save your changes to this page and redraw the Web page. You should now see a panel with text directions just below your map. If you don't see the text directions, check the console window for any error messages.

Information Windows

In Figure 7, you see a rectangular window above the marker with address information for the restaurant. This is called an information window and is accessed by the user clicking on the marker. It can be dismissed using the “x” in the upper right corner of the window.

Figure 7: Add an informational window to provide a better description of the location
Figure 7: Add an informational window to provide a better description of the location

There are just a few steps needed to add an information window to a marker. Create a variable, named infoWindow, in your closure and assign a null value to it for right now.

let infoWindow = null;

In the initialize() create a new instance a google.maps.InfoWindow class and assign it to the infoWindow variable.

function initialize() {  
    // Create new InfoWindow if it is not already  
    if (infoWindow == null) {
        infoWindow = new google.maps.InfoWindow();  
    }
    
    // Rest of the code here
}

In the map definition literal object, add a new property named infoContent. In this variable is where you define the HTML and the text for what you want your information window to look like. Add this property and some different HTML for each marker in your locations array so you can see an information window for each marker.

locations.push({
    "latitude": 36.036020,
    "longitude": -86.787600,
    "title": "Paul's Training Company",  
    "marker": null,  
    "infoContent": "<div class='info-window'><address>Paul's Training Company<br/>117 Franklin Rd<br/>Brentwood, TN 37027</address></div>"
});

Create an addInfoWindow() function in which you add an event listener to the marker you created earlier. On the click event of the marker call the setContent() method of the information window passing in the HTML you defined in the map definition object. Call the open() method on the infoWindow variable passing in the map object and the marker object. This is what displays the information window above the marker.

function addInfoWindow(location) {  
    // Add click event to marker  
    google.maps.event.addListener(location.marker, 'click', function () {
        // Add HTML content for window    
        infoWindow.setContent(location.infoContent);    
        
        // Open the window    
        infoWindow.open(map, location.marker);  
    });
}

The addInfoWindow() function needs to be called after you have created your marker, as you are going to set up the click event on each marker you add to the map. Locate the loop in the initialize() function and add a call to the addInfoWindow() function after the call to the addMarker() function.

for (let index = 0; index < locations.length; index++) {  
    // Add marker for location  
    addMarker(locations[index]);
    
    // Add info window for location  
    addInfoWindow(locations[index]);
    
    // Extend boundaries to encompass marker  
    bounds.extend(locations[index].marker.position);
}

Try It Out

Save your changes to this page and click on one of the locations. You should see an information window pop up above the marker. Now, click on another marker and the first information window disappears and one displays above the new marker you just clicked on. The reason for this toggling is that there's only one variable to hold an information window for the map object. If you change the variable, the original information window is destroyed and the new one appears.

Keep All Information Windows Open

Instead of only having a single information window appear at a time, you can keep them all open until the user manually closes each one. Remove the single instance of the infoWindow variable within your closure.

let infoWindow = null;

Add an infoWindow property to each location object and set the initial value to null.

locations.push({  
    "latitude": 36.036020,  
    "longitude": -86.787600,
    "marker": null,
    "infoWindow" : null,  
    "title": "Paul's Training Company",
    "infoContent": "content here"
});

Remove the code that creates the instance of the single infoWindow variable from within the initialize() function.

function initialize() {  
    // Create new InfoWindow if it is not already  
    if (infoWindow == null) {    
        infoWindow = new google.maps.InfoWindow();  
    }
    
    // Rest of the code here
}

Locate the addInfoWindow() function and add a line of code to set the new infoWindow property on the location object to a new instance of a google.maps.InfoWindow class. Within the addListener() function be sure to call the setContent() and open() methods on the infoWindow property of the location object you pass in.

function addInfoWindow(location) {  
    location.infoWindow = new google.maps.InfoWindow();
    
    // Add click event to marker
    google.maps.event.addListener(location.marker, 'click', function () {
        // Add HTML content for window    
        location.infoWindow.setContent(location.infoContent);    
        
        // Open the window    
        location.infoWindow.open(map, location.marker);  
    });
}

Try It Out

Save your changes and when you now click on each marker, the previous information windows stay visible. You can choose which method you like better and use the appropriate code.

Summary

In this article, you learned to use the HTML 5 geolocation object to retrieve a user's current latitude and longitude. Most modern browsers support this object, so you shouldn't have any issues using this. The user must grant the usage of this object, so be sure to handle the case where your user doesn't want to give you their position. In addition to this object, you learned the basics of working with the Google Maps API. This API isn't free to use, but it's one of the best APIs available for mapping. You saw how to embed a map, display locations, and information windows using this API. There are many more APIs available, such as driving directions, geofencing, and much more. Take some time to explore the many different options.

Table 1: You may set any or none of the PositionOptions.

PropertyDescription
enableHighAccuracyIf set to `true`, informs the device to attempt to obtain a more accurate position. This can slow down the time it takes to retrieve the coordinates.
timeoutThe maximum length of time to wait before throwing an exception. This value is expressed in milliseconds.
maximumAgeThe maximum age of the last cached position that's acceptable to return. This value is expressed in milliseconds. Set to 0 to not allow the device to cache positions.