Place Search Element

  • The PlaceListElement displays place search results in a list format within a Google Map.

  • It supports two search modes: text search using configureFromSearchByTextRequest and nearby search using configureFromSearchNearbyRequest.

  • When a place is selected from the list, an info window with place details is shown on the map.

  • This feature is experimental and might have limited support.

Select platform: Android iOS JavaScript

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:

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:600px;  box-sizing: border-box;  width: 100%;  height: 100vh;  }  .controls {  position: absolute;  top: 40px;  right: 40px;  }  .list-container {  display: flex;  position: absolute;  max-height: 500px;  top: 80px;  right: 40px;  overflow-y: none;  }  .type-select {  width: 400px;  height: 32px;  border: 1px solid #000;  border-radius: 10px;  flex-grow: 1;  padding: 0 10px;  }  gmp-place-search {  width: 400px;  margin: 0;  border-radius: 10px;  display: none;  border: none;  }  gmp-place-details-compact {  width: 350px;  max-height: 800px;  margin-right: 20px;  display: none;  border: none;  }  gmp-place-details-compact::after {  content: '';  position: absolute;  bottom: -18px;  left: 50%;  transform: translateX(-50%);  width: 20px;  height: 20px;  background-color: white;  box-shadow: 2px 2px 5px 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:600px;  box-sizing: border-box;  width: 100%;  height: 100vh;  }  .controls {  border-radius: 5px;  position: absolute;  top: 40px;  right: 40px;  }  .search-button {  background-color: #4b4b4b;  color: #fff;  border: 1px solid #000;  border-radius: 10px;  width: 80px;  height: 40px;  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.35);  }  .query-input {  border: 1px solid #ccc;  border-radius: 10px;  width: 315px;  height: 40px;  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.35);  }  .list-container {  display: flex;  position: absolute;  max-height: 500px;  top: 100px;  right: 40px;  overflow-y: none;  }  gmp-place-search {  width: 400px;  margin: 0;  border-radius: 10px;  display: none;  border: none;  }  gmp-place-details-compact {  width: 350px;  max-height: 800px;  display: none;  border: none;  transform: translateY(calc(-40%));  }  gmp-place-details-compact::after {  content: '';  position: absolute;  bottom: -18px;  left: 50%;  transform: translateX(-50%);  width: 20px;  height: 20px;  background-color: white;  box-shadow: 2px 2px 5px 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>