This example demonstrates the use of click event listeners on POIs (points of
interest). It listens for the click
event on a POI icon and then
uses the placeId
from the event data with a
directionsService.route
request to calculate and display a route to
the clicked place. It also uses the placeId
to get more details of
the place.
Read the documentation.
TypeScript
function initMap(): void { const origin = { lat: -33.871, lng: 151.197 }; const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { zoom: 18, center: origin, } ); new ClickEventHandler(map, origin); } function isIconMouseEvent( e: google.maps.MapMouseEvent | google.maps.IconMouseEvent ): e is google.maps.IconMouseEvent { return "placeId" in e; } class ClickEventHandler { origin: google.maps.LatLngLiteral; map: google.maps.Map; directionsService: google.maps.DirectionsService; directionsRenderer: google.maps.DirectionsRenderer; placesService: google.maps.places.PlacesService; infowindow: google.maps.InfoWindow; infowindowContent: HTMLElement; constructor(map: google.maps.Map, origin: google.maps.LatLngLiteral) { this.origin = origin; this.map = map; this.directionsService = new google.maps.DirectionsService(); this.directionsRenderer = new google.maps.DirectionsRenderer(); this.directionsRenderer.setMap(map); this.placesService = new google.maps.places.PlacesService(map); this.infowindow = new google.maps.InfoWindow(); this.infowindowContent = document.getElementById( "infowindow-content" ) as HTMLElement; this.infowindow.setContent(this.infowindowContent); // Listen for clicks on the map. this.map.addListener("click", this.handleClick.bind(this)); } handleClick(event: google.maps.MapMouseEvent | google.maps.IconMouseEvent) { console.log("You clicked on: " + event.latLng); // If the event has a placeId, use it. if (isIconMouseEvent(event)) { console.log("You clicked on place:" + event.placeId); // Calling e.stop() on the event prevents the default info window from // showing. // If you call stop here when there is no placeId you will prevent some // other map click event handlers from receiving the event. event.stop(); if (event.placeId) { this.calculateAndDisplayRoute(event.placeId); this.getPlaceInformation(event.placeId); } } } calculateAndDisplayRoute(placeId: string) { const me = this; this.directionsService.route( { origin: this.origin, destination: { placeId: placeId }, travelMode: google.maps.TravelMode.WALKING, }, (response, status) => { if (status === "OK") { me.directionsRenderer.setDirections(response); } else { window.alert("Directions request failed due to " + status); } } ); } getPlaceInformation(placeId: string) { const me = this; this.placesService.getDetails( { placeId: placeId }, ( place: google.maps.places.PlaceResult | null, status: google.maps.places.PlacesServiceStatus ) => { if ( status === "OK" && place && place.geometry && place.geometry.location ) { me.infowindow.close(); me.infowindow.setPosition(place.geometry.location); (me.infowindowContent.children[ "place-icon" ] as HTMLImageElement).src = place.icon as string; (me.infowindowContent.children[ "place-name" ] as HTMLElement).textContent = place.name!; (me.infowindowContent.children[ "place-id" ] as HTMLElement).textContent = place.place_id as string; (me.infowindowContent.children[ "place-address" ] as HTMLElement).textContent = place.formatted_address as string; me.infowindow.open(me.map); } } ); } }
JavaScript
function initMap() { const origin = { lat: -33.871, lng: 151.197 }; const map = new google.maps.Map(document.getElementById("map"), { zoom: 18, center: origin, }); new ClickEventHandler(map, origin); } function isIconMouseEvent(e) { return "placeId" in e; } class ClickEventHandler { constructor(map, origin) { this.origin = origin; this.map = map; this.directionsService = new google.maps.DirectionsService(); this.directionsRenderer = new google.maps.DirectionsRenderer(); this.directionsRenderer.setMap(map); this.placesService = new google.maps.places.PlacesService(map); this.infowindow = new google.maps.InfoWindow(); this.infowindowContent = document.getElementById("infowindow-content"); this.infowindow.setContent(this.infowindowContent); // Listen for clicks on the map. this.map.addListener("click", this.handleClick.bind(this)); } handleClick(event) { console.log("You clicked on: " + event.latLng); // If the event has a placeId, use it. if (isIconMouseEvent(event)) { console.log("You clicked on place:" + event.placeId); // Calling e.stop() on the event prevents the default info window from // showing. // If you call stop here when there is no placeId you will prevent some // other map click event handlers from receiving the event. event.stop(); if (event.placeId) { this.calculateAndDisplayRoute(event.placeId); this.getPlaceInformation(event.placeId); } } } calculateAndDisplayRoute(placeId) { const me = this; this.directionsService.route( { origin: this.origin, destination: { placeId: placeId }, travelMode: google.maps.TravelMode.WALKING, }, (response, status) => { if (status === "OK") { me.directionsRenderer.setDirections(response); } else { window.alert("Directions request failed due to " + status); } } ); } getPlaceInformation(placeId) { const me = this; this.placesService.getDetails({ placeId: placeId }, (place, status) => { if ( status === "OK" && place && place.geometry && place.geometry.location ) { me.infowindow.close(); me.infowindow.setPosition(place.geometry.location); me.infowindowContent.children["place-icon"].src = place.icon; me.infowindowContent.children["place-name"].textContent = place.name; me.infowindowContent.children["place-id"].textContent = place.place_id; me.infowindowContent.children["place-address"].textContent = place.formatted_address; me.infowindow.open(me.map); } }); } }
CSS
/* Always set the map height explicitly to define the size of the div * element that contains the map. */ #map { height: 100%; } /* Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } .title { font-weight: bold; } #infowindow-content { display: none; } #map #infowindow-content { display: inline; }
HTML
<!DOCTYPE html> <html> <head> <title>POI Click Events</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <link rel="stylesheet" type="text/css" href="./style.css" /> <script src="./index.js"></script> </head> <body> <div id="map"></div> <div id="infowindow-content"> <img id="place-icon" src="" height="16" width="16" /> <span id="place-name" class="title"></span><br /> Place ID <span id="place-id"></span><br /> <span id="place-address"></span> </div> <!-- Async script executes immediately and must be after any DOM elements used in callback. --> <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=places&v=weekly" async ></script> </body> </html>
All
<!DOCTYPE html> <html> <head> <title>POI Click Events</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <style type="text/css"> /* Always set the map height explicitly to define the size of the div * element that contains the map. */ #map { height: 100%; } /* Optional: Makes the sample page fill the window. */ html, body { height: 100%; margin: 0; padding: 0; } .title { font-weight: bold; } #infowindow-content { display: none; } #map #infowindow-content { display: inline; } </style> <script> function initMap() { const origin = { lat: -33.871, lng: 151.197 }; const map = new google.maps.Map(document.getElementById("map"), { zoom: 18, center: origin, }); new ClickEventHandler(map, origin); } function isIconMouseEvent(e) { return "placeId" in e; } class ClickEventHandler { constructor(map, origin) { this.origin = origin; this.map = map; this.directionsService = new google.maps.DirectionsService(); this.directionsRenderer = new google.maps.DirectionsRenderer(); this.directionsRenderer.setMap(map); this.placesService = new google.maps.places.PlacesService(map); this.infowindow = new google.maps.InfoWindow(); this.infowindowContent = document.getElementById( "infowindow-content" ); this.infowindow.setContent(this.infowindowContent); // Listen for clicks on the map. this.map.addListener("click", this.handleClick.bind(this)); } handleClick(event) { console.log("You clicked on: " + event.latLng); // If the event has a placeId, use it. if (isIconMouseEvent(event)) { console.log("You clicked on place:" + event.placeId); // Calling e.stop() on the event prevents the default info window from // showing. // If you call stop here when there is no placeId you will prevent some // other map click event handlers from receiving the event. event.stop(); if (event.placeId) { this.calculateAndDisplayRoute(event.placeId); this.getPlaceInformation(event.placeId); } } } calculateAndDisplayRoute(placeId) { const me = this; this.directionsService.route( { origin: this.origin, destination: { placeId: placeId }, travelMode: google.maps.TravelMode.WALKING, }, (response, status) => { if (status === "OK") { me.directionsRenderer.setDirections(response); } else { window.alert("Directions request failed due to " + status); } } ); } getPlaceInformation(placeId) { const me = this; this.placesService.getDetails( { placeId: placeId }, (place, status) => { if ( status === "OK" && place && place.geometry && place.geometry.location ) { me.infowindow.close(); me.infowindow.setPosition(place.geometry.location); me.infowindowContent.children["place-icon"].src = place.icon; me.infowindowContent.children["place-name"].textContent = place.name; me.infowindowContent.children["place-id"].textContent = place.place_id; me.infowindowContent.children["place-address"].textContent = place.formatted_address; me.infowindow.open(me.map); } } ); } } </script> </head> <body> <div id="map"></div> <div id="infowindow-content"> <img id="place-icon" src="" height="16" width="16" /> <span id="place-name" class="title"></span><br /> Place ID <span id="place-id"></span><br /> <span id="place-address"></span> </div> <!-- Async script executes immediately and must be after any DOM elements used in callback. --> <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=places&v=weekly" async ></script> </body> </html>
Create a starter application from sample
A skeleton starter application using TypeScript, Webpack, and Babel can be generated from this sample using one of the methods below.
Run Locally
Node.js is required to run this sample locally. Follow these instructions to install Node.js and NPM.
npx @googlemaps/js-samples init event-poi DESTINATION_FOLDER
Run in Google Cloud Shell
Google Cloud Shell is an interactive shell environment for Google Cloud Platform that makes it easy for you to learn and experiment with GCP and manage your projects and resources from your web browser.
Run in Cloud Shell