This content originally appeared on Envato Tuts+ Tutorials and was authored by George Martsoukos
In this new tutorial, we’ll use the Google Maps JavaScript API and learn how to create a custom map, with multiple stylish markers indicating some of the Adobe office locations.
As usual, to better understand what we’re going to build, look at the demo page. Be sure to click either on the locations or the map markers.
Custom Google Map Demo
Take a look at this fully interactive custom Google map we’re going to create.
Project Structure
For this exercise, our custom Google Map demo will live on GitHub (not on CodePen). Here’s the project structure:
map/ ├── index.html ├── img/ │ ├── address.svg │ ├── modal-fax.svg │ ├── modal-lat-long.svg │ ├── modal-pin.svg │ ├── modal-tel.svg │ └── pin.svg ├── main.css └── main.js
1. Scaffolding the Project
Before we start creating our project (that will look like a mini app) there are a few things that we have to take into consideration.
Grab a Google Maps API key
As a first and mandatory thing, we should get a Google Maps API key. To do so, we need to follow these instructions and set up a project in the Google Cloud Console with the Maps JavaScript API enabled.
For this demonstration, we’ll borrow an API key from an old yet still popular series called The Google Maps API For Designers. There’s also an associated demo from where we can extract the API.
Last but not least, as soon as you set up such a project in the Google Cloud Console, it’s always wise to restrict the associated API. For example, you might have a single project and an API that you share across all your website clients. In such a case, you can restrict the API requests to specific websites.
Grab Some Data
To create the markers and make our project as realistic as possible, we’ll need some real-world data. With this in mind, as previously mentioned, we’ll take 13 of the Adobe office locations and pin them on our map.
As we’ll see later, each location needs to include its latitude and longitude. As this info isn’t available on Adobe’s contact page, we’ll use the LangLong.net website to retrieve their coordinates. Some of the coordinates might not be perfectly accurate, but you get the point.
Grab Some Icons
To enhance the demo appearance, we’ll need some icons.
Envato Elements provides hundreds of different map and navigation icons for our needs. In this case, we’ll go with an icon set that follows a filled line style. As we saw from the project structure, the selected SVG icons will live inside the img
folder.
Here are the SVG icons we’ll use in our project:
Include Bootstrap Files
Lastly, although not required, to speed up the development process we’ll also include Bootstrap in our project by following the CDN approach, as described on this page.
2. Define the Page Markup
Now that we have everything set up, let’s look at our markup.
Required Files
Below you can see the starting markup with all the required Bootstrap files, our files, and the script
tag for the Maps JavaScript API:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>A simple example showing how to build a stylish Google Map with multiple markers</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous" /> <link rel="stylesheet" href="main.css" /> </head> <body> <!-- content here --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBSJRUNeSfHkH_ChC38jKTvjc2V7QQcOYs&language=en&callback=initMap" async></script> <script src="main.js"></script> </body> </html>
Just take a look at the script
tag for the Maps JavaScript API. You’ll see two attributes:
- The
src
attribute includes the base call to the Maps JavaScript API along with three parameters: two required and one optional. The requiredkey
parameter stores our API key as retrieved from the Google Cloud Console. The optionallanguage
parameter determines the map’s language (location names, labels, etc.). The requiredcallback
parameter defines the name of the global function that will fire once the Maps JavaScript API finishes its load. - The
async
parameter tells the browser to asynchronously download and execute the script.
Custom Google Map Layout
Before having a closer look at the page elements, let’s discuss the layout requirements.
- On mobile screens (<768px), the layout will be like this:
- On medium and large screens (≥768px), there will be two equal-width columns:
- In each case, the map will cover the window height.
Here’s the associated markup filled with Bootstrap helper classes:
<div class="container-fluid"> <div class="row align-items-center"> <div class="col-md-6 mt-5 mb-3"> <h1 class="mb-4"> Some of <a href="https://www.adobe.com/about-adobe/contact/offices.html" class="text-body" target="_blank">Adobe's office locations</a> </h1> <div class="block"> <h2 class="mb-3">North America</h2> <div class="row"> <div class="col-sm-6 mb-2"> <div class="d-flex align-items-center"> <img width="24" height="24" src="img/address.svg" alt="" class="me-2" /> <a href="" class="location text-body" data-index="0">Corporate headquarters</a> </div> </div> <div class="col-sm-6 mb-2"> <div class="d-flex align-items-center"> <img width="24" height="24" src="img/address.svg" alt="" class="me-2" /> <a href="" class="location text-body" data-index="1">Los Angeles</a> </div> </div> </div> <hr /> </div> <!-- more blocks here --> </div> <div class="col-md-6 p-0"> <div id="map" class="vh-100"></div> </div> </div> </div>
Most importantly, notice two things:
- Each location link, which represents an Adobe location, comes with the
data-index
attribute. Keep this attribute in mind, as we’ll use it later on. - There’s an empty element with the
map
ID. This will include the map contents and will be generated through JavaScript.
3. Add the JavaScript
At this point, we’re ready to build the core functionality of our custom Google Map project. Let’s do it!
Store Locations
We captured the locations in the markup, but we also need them in JavaScript. So, let’s store them under an array like this:
const pins = [ { location: "North America", name: "Corporate headquarters", address: "345 Park Avenue <br> San Jose, CA 95110-2704", tel: "408-536-2800", fax: "408-537-6000", lat: 37.33078, long: -121.892357, }, { location: "North America", name: "Los Angeles", address: "(former Magento office) <br> 3640 Holdrege Ave <br> Los Angeles, CA 90016", lat: 34.01989, long: -118.37811, }, // more locations here ];
Keep in mind that we preserve the order in which the locations appear both in the markup and object. A location with the data-index="0"
in the markup denotes that the same location should be in the first place of the pins
array.
Note: in a real-world scenario, there would probably be a backend language to manage all locations in one place. For example, if you’re familiar with WordPress and the ACF PRO plugin, you’ll probably have a flexible content or a repeater field where you put all locations. Inside it, there will be extra fields for managing the location details. Especially for grabbing the coordinates of each location, you can have a Google Map field. Then, via the wp_localize_script()
you’re able to pass the locations in the JavaScript and build an object similar to the one we have here.
Initialize Custom Google Map
Coming up next, we’ll initialize the map through the initMap()
that we showed before.
Our map will have the following customizations:
- It’ll be centered in London, UK.
- It’ll have
zoom: 3
to see as many locations as possible by default. - We’ll customize its default styles through this tool, but you can always opt for cloud-based map styling. Most notably, we’ll change the default color of the water like this:
With all the above in mind, here’s the starting body of our function:
// London, UK const center = { lat: 51.507351, lng: -0.127758 }; const styles = [ { featureType: "poi", stylers: [ { visibility: "off" } ] }, { featureType: "water", stylers: [ { color: "#39C3FC" } ] } ]; function initMap() { const map = new google.maps.Map(document.getElementById("map"), { center, styles, zoom: 3 }); }
Create Google Map Markers
Inside the initMap()
function, we’ll also call the createMarkers()
function for creating the location markers:
function initMap() { // map declaration here createMarkers(map); }
Inside this function, we’ll do the following things:
- Initialize an info window that will display information about a marker each time someone clicks on it.
- Replace the default marker icon with a custom SVG one.
- Loop through the locations and position them on the map based on their coordinates. Also, make them appear with a DROP animation.
- Store each marker instance in the
markers
array. We’ll see why later.
Here’s the function declaration:
function createMarkers(map) { const infowindow = new google.maps.InfoWindow(); const markerIcon = { url: "img/pin.svg", scaledSize: new google.maps.Size(40, 40) }; for (let i = 0; i < pins.length; i++) { const marker = new google.maps.Marker({ position: { lat: pins[i].lat, lng: pins[i].long }, map, icon: markerIcon, animation: google.maps.Animation.DROP }); markers.push(marker); } }
Toggle Info Window
Inside the createMarkers()
function, we’ll also register a click
event for each marker.
function createMarkers(map) { ... for (let i = 0; i < pins.length; i++) { // marker here google.maps.event.addListener(marker, "click", function () { infowindow.setContent(createInfoWindowContent(pins[i])); map.setCenter(marker.getPosition()); infowindow.open(map, marker); const targetLocation = document.querySelector(`[data-index="${i}"]`); if (document.querySelector(".location.active")) { document .querySelector(".location.active") .classList.remove(activeClass); } targetLocation.classList.add(activeClass); scroll({ top: targetLocation.offsetTop, behavior: "smooth" }); }); } }
Each time a user clicks on a marker, we’ll perform the following actions:
- Populate the info window’s content with contents associated with this marker thanks to the
createInfoContent()
function. - Set the map’s center based on the coordinates of this marker.
- Show the info window.
- Remove the
active
class from any associated location link, if any. - Find the location link whose index matches the index of this marker and give it the
active
class. This will give the target link a blue background color.
- Smooth scroll to the corresponding location link.
Populate Info Window
As we said, the job of the createInfoContent()
function will be to feed the info window with the contents of the clicked marker.
We’ll use straightforward markup to display the contents of each marker. Given that some locations don’t always have all the details, we’ll apply some checks to ensure that our markup won’t be bloated.
Here’s the function declaration:
function createInfoWindowContent(pin) { let phoneString = ""; let faxString = ""; let latLongString = ""; let addressString = ""; if (pin.tel) { phoneString = ` <p class="d-flex align-items-center"> <img class="me-2" width="24" height="24" src="img/modal-tel.svg" alt=""> ${pin.tel} </p> `; } if (pin.fax) { faxString = ` <p class="d-flex align-items-center"> <img class="me-2" width="24" height="24" src="img/modal-fax.svg" alt=""> ${pin.fax} </p> `; } if (pin.lat && pin.long) { latLongString = ` <p class="d-flex align-items-center"> <img class="me-2" width="24" height="24" src="img/modal-lat-long.svg" alt=""> ${pin.lat}, ${pin.long} </p> `; } if (pin.address) { addressString = ` <div class="d-flex"> <img class="me-2" width="24" height="24" src="img/modal-pin.svg" alt=""> ${pin.address} </div> `; } const contentString = ` <h3 class="fs-4 text">${pin.name}</h3> <hr> ${phoneString} ${faxString} ${latLongString} ${addressString} `; return contentString; }
Location Links
We discussed what should happen when a marker is clicked. But we also need to apply some functionality when the opposite happens. In other words, we want to ensure that each time a user clicks on a location link, the corresponding marker will be clicked.
Another function, the showLocations()
one, will be responsible for handling this functionality. This will also live inside the initMap()
function.
So, each time a location link is clicked, we’ll perform the following actions:
- Cancel its default behavior.
- Remove the
active
class from any location link, if any. - Add the
active
class to this link. - Smooth scroll to the map.
- Find the marker whose index matches the index of this link and trigger its
click
event. We’re able to target the specified marker because we’ve stored it in a previous step inside themarkers
array.
Here’s the function definition:
function showLocations() { const locations = document.querySelectorAll(".location"); locations.forEach((location) => { location.addEventListener("click", function (e) { e.preventDefault(); if (document.querySelector(".location.active")) { document .querySelector(".location.active") .classList.remove(activeClass); } location.classList.add(activeClass); scroll({ top: document.getElementById("map").offsetTop, behavior: "smooth" }); new google.maps.event.trigger(markers[this.dataset.index], "click"); }); }); }
Conclusion
And we’re done! This was quite a long journey, but I hope that you enjoyed it and that it has helped enhance your skills regarding the Google Maps JavaScript API.
Once again, don’t forget to put your own key for live project testing! Here’s the project link.
Things don’t stop here. We can continue enhancing/extending this project by doing a number of things, for instance:
- Put into table marker clusters for grouping markers. This is a nice addition, especially for cases where there are a lot of markers.
- Make this project dynamic by using WordPress and ACF PRO, as explained in a few sections above. Depending on how this project goes, I’ll probably come back with such a tutorial in the future :)
- Restrict the map boundaries to avoid showing a gray area during dragging or zooming out.
As always, thanks a lot for reading!
This content originally appeared on Envato Tuts+ Tutorials and was authored by George Martsoukos
George Martsoukos | Sciencx (2022-09-12T07:01:38+00:00) How to Build a Custom Google Map With Stylish SVG Markers. Retrieved from https://www.scien.cx/2022/09/12/how-to-build-a-custom-google-map-with-stylish-svg-markers/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.