The PlaceSearchElement
is an HTML element that renders the results of a place search in a list. There are two ways to
configure the gmp-place-search
element:
- Use search nearby request
to render search results using the
PlaceNearbySearchRequestElement
- Use search by text request
to render search results using
the
PlaceTextSearchRequestElement
Search nearby request
Select a place type from the menu to see nearby search results for that place type.
The following example renders the Place
Search element in response to a nearby search. For simplicity, only three place types are listed:
cafe, restaurant, and EV charging station. When a result is selected, a marker and PlaceDetailsCompactElement
are displayed for the selected place. To add the
Place Search element to the map, add a gmp-place-search
element containing a gmp-place-nearby-search-request
element to the HTML page as shown in the
following snippet:
<div class="list-container"> <div id="map-container"></div> <div class="controls"> <select name="types" class="type-select"> <option value="">Select a place type</option> <option value="cafe">Cafe</option> <option value="restaurant">Restaurant</option> <option value="electric_vehicle_charging_station">EV charging station</option> </select> </div> <div class="list-container"> <gmp-place-search orientation="vertical" selectable> <gmp-place-all-content> </gmp-place-all-content> <gmp-place-nearby-search-request ></gmp-place-nearby-search-request> </gmp-place-search> </div> <div id="details-container"> <gmp-place-details-compact orientation="horizontal"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details-compact> </div>
Several querySelector
calls are used to select the page elements for interaction:
const mapContainer = document . getElementById ( "map-container" ); const placeSearch = document . querySelector ( "gmp-place-search" ); const placeSearchQuery = document . querySelector ( "gmp-place-nearby-search-request" ); const detailsContainer = document . getElementById ( "details-container" ); const placeDetails = document . querySelector ( "gmp-place-details-compact" ); const placeRequest = document . querySelector ( "gmp-place-details-place-request" ); const typeSelect = document . querySelector ( ".type-select" );
When the user selects a place type from the menu, the gmp-place-nearby-search-request
element is updated, and the Place Search element
displays the results (markers are added in the addMarkers
helper function):
typeSelect . addEventListener ( 'change' , ( event ) => { event . preventDefault (); searchPlaces (); }); function searchPlaces (){ const bounds = gMap . getBounds (); placeDetailsPopup . map = null ; if ( typeSelect . value ) { placeSearch . style . display = 'block' ; placeSearchQuery . maxResultCount = 10 ; placeSearchQuery . locationRestriction = { center : cent , radius : 1000 }; placeSearchQuery . includedTypes = [ typeSelect . value ]; placeSearch . addEventListener ( 'gmp-load' , addMarkers , { once : true }); } }
See the complete code example
JavaScript
const mapContainer = document . getElementById ( "map-container" ); const placeSearch = document . querySelector ( "gmp-place-search" ); const placeSearchQuery = document . querySelector ( "gmp-place-nearby-search-request" ); const detailsContainer = document . getElementById ( "details-container" ); const placeDetails = document . querySelector ( "gmp-place-details-compact" ); const placeRequest = document . querySelector ( "gmp-place-details-place-request" ); const typeSelect = document . querySelector ( ".type-select" ); let markers = {}; let gMap ; let placeDetailsPopup ; let spherical ; let AdvancedMarkerElement ; let LatLngBounds ; let LatLng ; async function init () { console . log ( "init" ); ({ spherical } = await google . maps . importLibrary ( 'geometry' )); const { Map } = await google . maps . importLibrary ( "maps" ); await google . maps . importLibrary ( "places" ); ({ AdvancedMarkerElement } = await google . maps . importLibrary ( "marker" )); ({ LatLngBounds , LatLng } = await google . maps . importLibrary ( "core" )); let mapOptions = { center : { lat : - 37.813 , lng : 144.963 }, zoom : 16 , mapTypeControl : false , clickableIcons : false , mapId : 'DEMO_MAP_ID' }; gMap = new Map ( mapContainer , mapOptions ); placeDetailsPopup = new AdvancedMarkerElement ({ map : null , content : placeDetails , zIndex : 100 }); findCurrentLocation (); gMap . addListener ( 'click' , ( e ) => { hidePlaceDetailsPopup (); }); typeSelect . addEventListener ( 'change' , ( event ) => { event . preventDefault (); searchPlaces (); }); placeSearch . addEventListener ( "gmp-select" , ({ place }) => { if ( markers [ place . id ]) { markers [ place . id ]. click (); } }); } function searchPlaces (){ const bounds = gMap . getBounds (); const cent = gMap . getCenter (); const ne = bounds . getNorthEast (); const sw = bounds . getSouthWest (); const diameter = spherical . computeDistanceBetween ( ne , sw ); const cappedRadius = Math . min (( diameter / 2 ), 50000 ); // Radius cannot be more than 50000. placeDetailsPopup . map = null ; for ( const markerId in markers ){ if ( Object . prototype . hasOwnProperty . call ( markers , markerId )) { markers [ markerId ]. map = null ; } } markers = {}; if ( typeSelect . value ) { mapContainer . style . height = '75vh' ; placeSearch . style . display = 'block' ; placeSearchQuery . maxResultCount = 10 ; placeSearchQuery . locationRestriction = { center : cent , radius : cappedRadius }; placeSearchQuery . includedTypes = [ typeSelect . value ]; placeSearch . addEventListener ( 'gmp-load' , addMarkers , { once : true }); console . log ( "selection!" ); console . log ( cappedRadius ); } } async function addMarkers (){ const bounds = new LatLngBounds (); placeSearch . style . display = 'block' ; if ( placeSearch . places . length > 0 ){ placeSearch . places . forEach (( place ) => { let marker = new AdvancedMarkerElement ({ map : gMap , position : place . location }); marker . metadata = { id : place . id }; markers [ place . id ] = marker ; bounds . extend ( place . location ); marker . addListener ( 'click' ,( event ) => { placeRequest . place = place ; placeDetails . style . display = 'block' ; placeDetailsPopup . position = place . location ; placeDetailsPopup . map = gMap ; gMap . fitBounds ( place . viewport , { top : 0 , left : 400 }); placeDetails . addEventListener ( 'gmp-load' ,() => { gMap . fitBounds ( place . viewport , { top : 0 , right : 450 }); }, { once : true }); }); gMap . setCenter ( bounds . getCenter ()); gMap . fitBounds ( bounds ); }); } } async function findCurrentLocation (){ const { LatLng } = await google . maps . importLibrary ( "core" ); if ( navigator . geolocation ) { navigator . geolocation . getCurrentPosition ( ( position ) => { const pos = new LatLng ( position . coords . latitude , position . coords . longitude ); gMap . panTo ( pos ); gMap . setZoom ( 16 ); }, () => { console . log ( 'The Geolocation service failed.' ); gMap . setZoom ( 16 ); }, ); } else { console . log ( "Your browser doesn't support geolocation" ); gMap . setZoom ( 16 ); } } function hidePlaceDetailsPopup () { if ( placeDetailsPopup . map ) { placeDetailsPopup . map = null ; placeDetails . style . display = 'none' ; } } init ();
CSS
html , body { height : 100 % ; margin : 0 ; } body { display : flex ; flex-direction : column ; font-family : Arial , Helvetica , sans-serif ; } h1 { font-size : large ; text-align : center ; } # map-container { flex-grow : 1 ; max-height : 600 px ; box-sizing : border-box ; width : 100 % ; height : 100 vh ; } . controls { position : absolute ; top : 40 px ; right : 40 px ; } . list-container { display : flex ; position : absolute ; max-height : 500 px ; top : 80 px ; right : 40 px ; overflow-y : none ; } . type-select { width : 400 px ; height : 32 px ; border : 1 px solid #000 ; border-radius : 10 px ; flex-grow : 1 ; padding : 0 10 px ; } gmp-place-search { width : 400 px ; margin : 0 ; border-radius : 10 px ; display : none ; border : none ; } gmp-place-details-compact { width : 350 px ; max-height : 800 px ; margin-right : 20 px ; display : none ; border : none ; } gmp-place-details-compact :: after { content : '' ; position : absolute ; bottom : -18 px ; left : 50 % ; transform : translateX ( -50 % ); width : 20 px ; height : 20 px ; background-color : white ; box-shadow : 2 px 2 px 5 px 0 rgba ( 0 , 0 , 0 , 0.2 ); z-index : 1 ; clip-path : polygon ( 0 % 0 % , 100 % 0 % , 50 % 100 % ); transform-origin : center center ; } @ media ( prefers-color-scheme : dark ) { /* Style for Dark mode */ gmp-place-details-compact :: after { background-color : #131314 ; } }
HTML
<!DOCTYPE html> <html> <head> <title>Place Search with Compact Place Details Element</title> <meta charset="utf-8"> <link rel="stylesheet" href="style.css"> <script type="module" src="./index.js"></script> </head> <body> <div id="map-container"></div> <div class="controls"> <select name="types" class="type-select"> <option value="">Select a place type</option> <option value="cafe">Cafe</option> <option value="restaurant">Restaurant</option> <option value="electric_vehicle_charging_station">EV charging station</option> </select> </div> <div class="list-container"> <gmp-place-search orientation="vertical" selectable> <gmp-place-all-content> </gmp-place-all-content> <gmp-place-nearby-search-request ></gmp-place-nearby-search-request> </gmp-place-search> </div> <div id="details-container"> <gmp-place-details-compact orientation="horizontal"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details-compact> </div> <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))}) ({key: "YOUR_API_KEY", v: "weekly"}); </script> </body> </html>
Search by text request
Enter a search term in the input field and click the Search button to get a list of places that match the term.
This example renders the Place Search element in response to a user text search. When a result is
selected, a marker and PlaceDetailsCompactElement
are displayed for the selected place. To add the
Place Search element to the map, add a gmp-place-search
element containing a gmp-place-search-text-search-request
element to the HTML page as shown in the
following snippet:
<div id="map-container"></div> <div class="controls"> <input type="text" class="query-input" /> <button class="search-button">Search</button> </div> <div class="list-container"> <gmp-place-search orientation="vertical" selectable> <gmp-place-all-content> </gmp-place-all-content> <gmp-place-text-search-request></gmp-place-text-search-request> </gmp-place-search> </div> <div id="details-container"> <gmp-place-details-compact orientation="horizontal"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details-compact> </div>
Several querySelector
calls are used to select the page elements for interaction:
const mapContainer = document . getElementById ( "map-container" ); const placeSearch = document . querySelector ( "gmp-place-search" ); const placeSearchQuery = document . querySelector ( "gmp-place-text-search-request" ); const queryInput = document . querySelector ( ".query-input" ); const searchButton = document . querySelector ( ".search-button" ); const detailsContainer = document . getElementById ( "details-container" ); const placeDetails = document . querySelector ( "gmp-place-details-compact" ); const placeRequest = document . querySelector ( "gmp-place-details-place-request" );
When the search function is run after the user enters a search query, the gmp-place-text-search-request
element is updated, and the Place Search element
displays the results (markers are added in the addMarkers
helper function):
searchButton . addEventListener ( "click" , searchPlaces ); queryInput . addEventListener ( "keydown" , ( event ) => { if ( event . key == 'Enter' ) { event . preventDefault (); searchPlaces (); } }); function searchPlaces (){ if ( queryInput . value ) { placeSearch . style . display = 'block' ; placeSearchQuery . textQuery = queryInput . value ; placeSearchQuery . locationBias = gMap . getBounds (); placeSearch . addEventListener ( 'gmp-load' , addMarkers , { once : true }); } }
See the complete code example
JavaScript
const mapContainer = document . getElementById ( "map-container" ); const placeSearch = document . querySelector ( "gmp-place-search" ); const placeSearchQuery = document . querySelector ( "gmp-place-text-search-request" ); const queryInput = document . querySelector ( ".query-input" ); const searchButton = document . querySelector ( ".search-button" ); const detailsContainer = document . getElementById ( "details-container" ); const placeDetails = document . querySelector ( "gmp-place-details-compact" ); const placeRequest = document . querySelector ( "gmp-place-details-place-request" ); let markers = {}; let previousSearchQuery = '' ; let gMap ; let placeDetailsPopup ; let AdvancedMarkerElement ; let LatLngBounds ; let LatLng ; async function init () { const { Map } = await google . maps . importLibrary ( "maps" ); await google . maps . importLibrary ( "places" ); ({ AdvancedMarkerElement } = await google . maps . importLibrary ( "marker" )); ({ LatLngBounds , LatLng } = await google . maps . importLibrary ( "core" )); let mapOptions = { center : { lat : 37.422 , lng : - 122.085 }, zoom : 2 , mapTypeControl : false , clickableIcons : false , mapId : 'DEMO_MAP_ID' }; gMap = new Map ( mapContainer , mapOptions ); placeDetailsPopup = new AdvancedMarkerElement ({ map : null , content : placeDetails , zIndex : 100 }); findCurrentLocation (); gMap . addListener ( 'click' , ( e ) => { hidePlaceDetailsPopup (); }); searchButton . addEventListener ( "click" , searchPlaces ); queryInput . addEventListener ( "keydown" , ( event ) => { if ( event . key == 'Enter' ) { event . preventDefault (); searchPlaces (); } }); placeSearch . addEventListener ( "gmp-select" , ({ place }) => { if ( markers [ place . id ]) { markers [ place . id ]. click (); } }); } function searchPlaces (){ if ( queryInput . value . trim () === previousSearchQuery ) { return ; } previousSearchQuery = queryInput . value . trim (); placeDetailsPopup . map = null ; for ( const markerId in markers ){ if ( Object . prototype . hasOwnProperty . call ( markers , markerId )) { markers [ markerId ]. map = null ; } } markers = {}; if ( queryInput . value ) { // mapContainer.style.height = '75vh'; placeSearch . style . display = 'block' ; placeSearchQuery . textQuery = queryInput . value ; placeSearchQuery . locationBias = gMap . getBounds (); placeSearch . addEventListener ( 'gmp-load' , addMarkers , { once : true }); } } async function addMarkers (){ const bounds = new LatLngBounds (); if ( placeSearch . places . length > 0 ){ placeSearch . places . forEach (( place ) => { let marker = new AdvancedMarkerElement ({ map : gMap , position : place . location }); marker . metadata = { id : place . id }; markers [ place . id ] = marker ; bounds . extend ( place . location ); marker . addListener ( 'click' ,( event ) => { placeRequest . place = place ; placeDetails . style . display = 'block' ; placeDetailsPopup . position = place . location ; placeDetailsPopup . map = gMap ; gMap . fitBounds ( place . viewport , { top : 200 , right : 450 }); }); gMap . setCenter ( bounds . getCenter ()); gMap . fitBounds ( bounds ); }); } } async function findCurrentLocation (){ const { LatLng } = await google . maps . importLibrary ( "core" ); if ( navigator . geolocation ) { navigator . geolocation . getCurrentPosition ( ( position ) => { const pos = new LatLng ( position . coords . latitude , position . coords . longitude ); gMap . panTo ( pos ); gMap . setZoom ( 16 ); }, () => { console . log ( 'The Geolocation service failed.' ); gMap . setZoom ( 16 ); }, ); } else { console . log ( "Your browser doesn't support geolocation" ); gMap . setZoom ( 16 ); } } function hidePlaceDetailsPopup () { if ( placeDetailsPopup . map ) { placeDetailsPopup . map = null ; placeDetails . style . display = 'none' ; } } init ();
CSS
html , body { height : 100 % ; margin : 0 ; } body { display : flex ; flex-direction : column ; font-family : Arial , Helvetica , sans-serif ; } h1 { font-size : large ; text-align : center ; } # map-container { flex-grow : 1 ; max-height : 600 px ; box-sizing : border-box ; width : 100 % ; height : 100 vh ; } . controls { border-radius : 5 px ; position : absolute ; top : 40 px ; right : 40 px ; } . search-button { background-color : #4b4b4b ; color : #fff ; border : 1 px solid #000 ; border-radius : 10 px ; width : 80 px ; height : 40 px ; box-shadow : 0 4 px 8 px rgba ( 0 , 0 , 0 , 0.35 ); } . query-input { border : 1 px solid #ccc ; border-radius : 10 px ; width : 315 px ; height : 40 px ; box-shadow : 0 4 px 8 px rgba ( 0 , 0 , 0 , 0.35 ); } . list-container { display : flex ; position : absolute ; max-height : 500 px ; top : 100 px ; right : 40 px ; overflow-y : none ; } gmp-place-search { width : 400 px ; margin : 0 ; border-radius : 10 px ; display : none ; border : none ; } gmp-place-details-compact { width : 350 px ; max-height : 800 px ; display : none ; border : none ; transform : translateY ( calc ( -40 % )); } gmp-place-details-compact :: after { content : '' ; position : absolute ; bottom : -18 px ; left : 50 % ; transform : translateX ( -50 % ); width : 20 px ; height : 20 px ; background-color : white ; box-shadow : 2 px 2 px 5 px 0 rgba ( 0 , 0 , 0 , 0.2 ); z-index : 1 ; clip-path : polygon ( 0 % 0 % , 100 % 0 % , 50 % 100 % ); transform-origin : center center ; } @ media ( prefers-color-scheme : dark ) { /* Style for Dark mode */ gmp-place-details-compact :: after { background-color : #131314 ; } }
HTML
<!DOCTYPE html> <html> <head> <title>Place Search with a Details Popup</title> <meta charset="utf-8"> <link rel="stylesheet" href="style.css"> <script type="module" src="./index.js"></script> </head> <body> <div id="map-container"></div> <div class="controls"> <input type="text" class="query-input" /> <button class="search-button">Search</button> </div> <div class="list-container"> <gmp-place-search orientation="vertical" selectable> <gmp-place-all-content> </gmp-place-all-content> <gmp-place-text-search-request></gmp-place-text-search-request> </gmp-place-search> </div> <div id="details-container"> <gmp-place-details-compact orientation="horizontal"> <gmp-place-details-place-request></gmp-place-details-place-request> <gmp-place-all-content></gmp-place-all-content> </gmp-place-details-compact> </div> <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))}) ({key: "YOUR_API_KEY", v: "weekly"}); </script> </body> </html>