It's the 15th anniversary of Google Maps Platform - Check out the latest news and announcements

Place Autocomplete Hotel Search


This example creates a map with a search box that finds all of the hotels in a specified place within a given country. The map then displays markers for all of the hotels returned, with on-click details for each hotel.

Read the documentation.

TypeScript

// This example uses the autocomplete feature of the Google Places API.
// It allows the user to find all hotels in a given place, within a given
// country. It then displays markers for all the hotels returned,
// with on-click details for each hotel.

// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">

let map: google.maps.Map;
let places: google.maps.places.PlacesService;
let infoWindow: google.maps.InfoWindow;
let markers: google.maps.Marker[] = [];
let autocomplete: google.maps.places.Autocomplete;

const countryRestrict = { country: "us" };
const MARKER_PATH =
  "https://developers.google.com/maps/documentation/javascript/images/marker_green";
const hostnameRegexp = new RegExp("^https?://.+?/");

const countries: Record<
  string,
  { center: google.maps.LatLngLiteral; zoom: number }
> = {
  au: {
    center: { lat: -25.3, lng: 133.8 },
    zoom: 4,
  },
  br: {
    center: { lat: -14.2, lng: -51.9 },
    zoom: 3,
  },
  ca: {
    center: { lat: 62, lng: -110.0 },
    zoom: 3,
  },
  fr: {
    center: { lat: 46.2, lng: 2.2 },
    zoom: 5,
  },
  de: {
    center: { lat: 51.2, lng: 10.4 },
    zoom: 5,
  },
  mx: {
    center: { lat: 23.6, lng: -102.5 },
    zoom: 4,
  },
  nz: {
    center: { lat: -40.9, lng: 174.9 },
    zoom: 5,
  },
  it: {
    center: { lat: 41.9, lng: 12.6 },
    zoom: 5,
  },
  za: {
    center: { lat: -30.6, lng: 22.9 },
    zoom: 5,
  },
  es: {
    center: { lat: 40.5, lng: -3.7 },
    zoom: 5,
  },
  pt: {
    center: { lat: 39.4, lng: -8.2 },
    zoom: 6,
  },
  us: {
    center: { lat: 37.1, lng: -95.7 },
    zoom: 3,
  },
  uk: {
    center: { lat: 54.8, lng: -4.6 },
    zoom: 5,
  },
};

function initMap(): void {
  map = new google.maps.Map(document.getElementById("map") as HTMLElement, {
    zoom: countries["us"].zoom,
    center: countries["us"].center,
    mapTypeControl: false,
    panControl: false,
    zoomControl: false,
    streetViewControl: false,
  });

  infoWindow = new google.maps.InfoWindow({
    content: document.getElementById("info-content") as HTMLElement,
  });

  // Create the autocomplete object and associate it with the UI input control.
  // Restrict the search to the default country, and to place type "cities".
  autocomplete = new google.maps.places.Autocomplete(
    document.getElementById("autocomplete") as HTMLInputElement,
    {
      types: ["(cities)"],
      componentRestrictions: countryRestrict,
    }
  );
  places = new google.maps.places.PlacesService(map);

  autocomplete.addListener("place_changed", onPlaceChanged);

  // Add a DOM event listener to react when the user selects a country.
  (document.getElementById("country") as HTMLSelectElement).addEventListener(
    "change",
    setAutocompleteCountry
  );
}

// When the user selects a city, get the place details for the city and
// zoom the map in on the city.
function onPlaceChanged() {
  const place = autocomplete.getPlace();

  if (place.geometry) {
    map.panTo(place.geometry.location);
    map.setZoom(15);
    search();
  } else {
    (document.getElementById("autocomplete") as HTMLInputElement).placeholder =
      "Enter a city";
  }
}

// Search for hotels in the selected city, within the viewport of the map.
function search() {
  const search = {
    bounds: map.getBounds() as google.maps.LatLngBounds,
    types: ["lodging"],
  };

  places.nearbySearch(
    search,
    (
      results: google.maps.places.PlaceResult[],
      status: google.maps.places.PlacesServiceStatus,
      pagination: google.maps.places.PlaceSearchPagination
    ) => {
      if (status === google.maps.places.PlacesServiceStatus.OK) {
        clearResults();
        clearMarkers();

        // Create a marker for each hotel found, and
        // assign a letter of the alphabetic to each marker icon.
        for (let i = 0; i < results.length; i++) {
          const markerLetter = String.fromCharCode(
            "A".charCodeAt(0) + (i % 26)
          );
          const markerIcon = MARKER_PATH + markerLetter + ".png";
          // Use marker animation to drop the icons incrementally on the map.
          markers[i] = new google.maps.Marker({
            position: (results[i].geometry as google.maps.places.PlaceGeometry)
              .location,
            animation: google.maps.Animation.DROP,
            icon: markerIcon,
          });
          // If the user clicks a hotel marker, show the details of that hotel
          // in an info window.
          // @ts-ignore TODO(jpoehnelt) refactor to avoid storing on marker
          markers[i].placeResult = results[i];
          google.maps.event.addListener(markers[i], "click", showInfoWindow);
          setTimeout(dropMarker(i), i * 100);
          addResult(results[i], i);
        }
      }
    }
  );
}

function clearMarkers() {
  for (let i = 0; i < markers.length; i++) {
    if (markers[i]) {
      markers[i].setMap(null);
    }
  }
  markers = [];
}

// Set the country restriction based on user input.
// Also center and zoom the map on the given country.
function setAutocompleteCountry() {
  const country = (document.getElementById("country") as HTMLInputElement)
    .value;

  if (country == "all") {
    autocomplete.setComponentRestrictions({ country: [] });
    map.setCenter({ lat: 15, lng: 0 });
    map.setZoom(2);
  } else {
    autocomplete.setComponentRestrictions({ country: country });
    map.setCenter(countries[country].center);
    map.setZoom(countries[country].zoom);
  }
  clearResults();
  clearMarkers();
}

function dropMarker(i) {
  return function () {
    markers[i].setMap(map);
  };
}

function addResult(result, i) {
  const results = document.getElementById("results") as HTMLElement;
  const markerLetter = String.fromCharCode("A".charCodeAt(0) + (i % 26));
  const markerIcon = MARKER_PATH + markerLetter + ".png";

  const tr = document.createElement("tr");
  tr.style.backgroundColor = i % 2 === 0 ? "#F0F0F0" : "#FFFFFF";

  tr.onclick = function () {
    google.maps.event.trigger(markers[i], "click");
  };

  const iconTd = document.createElement("td");
  const nameTd = document.createElement("td");
  const icon = document.createElement("img");
  icon.src = markerIcon;
  icon.setAttribute("class", "placeIcon");
  icon.setAttribute("className", "placeIcon");
  const name = document.createTextNode(result.name);
  iconTd.appendChild(icon);
  nameTd.appendChild(name);
  tr.appendChild(iconTd);
  tr.appendChild(nameTd);
  results.appendChild(tr);
}

function clearResults() {
  const results = document.getElementById("results") as HTMLElement;

  while (results.childNodes[0]) {
    results.removeChild(results.childNodes[0]);
  }
}

// Get the place details for a hotel. Show the information in an info window,
// anchored on the marker for the hotel that the user selected.
function showInfoWindow() {
  // @ts-ignore
  const marker = this;
  places.getDetails(
    { placeId: marker.placeResult.place_id },
    (place, status) => {
      if (status !== google.maps.places.PlacesServiceStatus.OK) {
        return;
      }
      infoWindow.open(map, marker);
      buildIWContent(place);
    }
  );
}

// Load the place information into the HTML elements used by the info window.
function buildIWContent(place) {
  (document.getElementById("iw-icon") as HTMLElement).innerHTML =
    '<img class="hotelIcon" ' + 'src="' + place.icon + '"/>';
  (document.getElementById("iw-url") as HTMLElement).innerHTML =
    '<b><a href="' + place.url + '">' + place.name + "</a></b>";
  (document.getElementById("iw-address") as HTMLElement).textContent =
    place.vicinity;

  if (place.formatted_phone_number) {
    (document.getElementById("iw-phone-row") as HTMLElement).style.display = "";
    (document.getElementById("iw-phone") as HTMLElement).textContent =
      place.formatted_phone_number;
  } else {
    (document.getElementById("iw-phone-row") as HTMLElement).style.display =
      "none";
  }

  // Assign a five-star rating to the hotel, using a black star ('&#10029;')
  // to indicate the rating the hotel has earned, and a white star ('&#10025;')
  // for the rating points not achieved.
  if (place.rating) {
    let ratingHtml = "";

    for (let i = 0; i < 5; i++) {
      if (place.rating < i + 0.5) {
        ratingHtml += "&#10025;";
      } else {
        ratingHtml += "&#10029;";
      }
      (document.getElementById("iw-rating-row") as HTMLElement).style.display =
        "";
      (document.getElementById(
        "iw-rating"
      ) as HTMLElement).innerHTML = ratingHtml;
    }
  } else {
    (document.getElementById("iw-rating-row") as HTMLElement).style.display =
      "none";
  }

  // The regexp isolates the first part of the URL (domain plus subdomain)
  // to give a short URL for displaying in the info window.
  if (place.website) {
    let fullUrl = place.website;
    let website = String(hostnameRegexp.exec(place.website));

    if (!website) {
      website = "http://" + place.website + "/";
      fullUrl = website;
    }
    (document.getElementById("iw-website-row") as HTMLElement).style.display =
      "";
    (document.getElementById(
      "iw-website"
    ) as HTMLElement).textContent = website;
  } else {
    (document.getElementById("iw-website-row") as HTMLElement).style.display =
      "none";
  }
}

JavaScript

// This example uses the autocomplete feature of the Google Places API.
// It allows the user to find all hotels in a given place, within a given
// country. It then displays markers for all the hotels returned,
// with on-click details for each hotel.
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
let map;
let places;
let infoWindow;
let markers = [];
let autocomplete;
const countryRestrict = { country: "us" };
const MARKER_PATH =
  "https://developers.google.com/maps/documentation/javascript/images/marker_green";
const hostnameRegexp = new RegExp("^https?://.+?/");
const countries = {
  au: {
    center: { lat: -25.3, lng: 133.8 },
    zoom: 4,
  },
  br: {
    center: { lat: -14.2, lng: -51.9 },
    zoom: 3,
  },
  ca: {
    center: { lat: 62, lng: -110.0 },
    zoom: 3,
  },
  fr: {
    center: { lat: 46.2, lng: 2.2 },
    zoom: 5,
  },
  de: {
    center: { lat: 51.2, lng: 10.4 },
    zoom: 5,
  },
  mx: {
    center: { lat: 23.6, lng: -102.5 },
    zoom: 4,
  },
  nz: {
    center: { lat: -40.9, lng: 174.9 },
    zoom: 5,
  },
  it: {
    center: { lat: 41.9, lng: 12.6 },
    zoom: 5,
  },
  za: {
    center: { lat: -30.6, lng: 22.9 },
    zoom: 5,
  },
  es: {
    center: { lat: 40.5, lng: -3.7 },
    zoom: 5,
  },
  pt: {
    center: { lat: 39.4, lng: -8.2 },
    zoom: 6,
  },
  us: {
    center: { lat: 37.1, lng: -95.7 },
    zoom: 3,
  },
  uk: {
    center: { lat: 54.8, lng: -4.6 },
    zoom: 5,
  },
};

function initMap() {
  map = new google.maps.Map(document.getElementById("map"), {
    zoom: countries["us"].zoom,
    center: countries["us"].center,
    mapTypeControl: false,
    panControl: false,
    zoomControl: false,
    streetViewControl: false,
  });
  infoWindow = new google.maps.InfoWindow({
    content: document.getElementById("info-content"),
  });
  // Create the autocomplete object and associate it with the UI input control.
  // Restrict the search to the default country, and to place type "cities".
  autocomplete = new google.maps.places.Autocomplete(
    document.getElementById("autocomplete"),
    {
      types: ["(cities)"],
      componentRestrictions: countryRestrict,
    }
  );
  places = new google.maps.places.PlacesService(map);
  autocomplete.addListener("place_changed", onPlaceChanged);
  // Add a DOM event listener to react when the user selects a country.
  document
    .getElementById("country")
    .addEventListener("change", setAutocompleteCountry);
}

// When the user selects a city, get the place details for the city and
// zoom the map in on the city.
function onPlaceChanged() {
  const place = autocomplete.getPlace();

  if (place.geometry) {
    map.panTo(place.geometry.location);
    map.setZoom(15);
    search();
  } else {
    document.getElementById("autocomplete").placeholder = "Enter a city";
  }
}

// Search for hotels in the selected city, within the viewport of the map.
function search() {
  const search = {
    bounds: map.getBounds(),
    types: ["lodging"],
  };
  places.nearbySearch(search, (results, status, pagination) => {
    if (status === google.maps.places.PlacesServiceStatus.OK) {
      clearResults();
      clearMarkers();

      // Create a marker for each hotel found, and
      // assign a letter of the alphabetic to each marker icon.
      for (let i = 0; i < results.length; i++) {
        const markerLetter = String.fromCharCode("A".charCodeAt(0) + (i % 26));
        const markerIcon = MARKER_PATH + markerLetter + ".png";
        // Use marker animation to drop the icons incrementally on the map.
        markers[i] = new google.maps.Marker({
          position: results[i].geometry.location,
          animation: google.maps.Animation.DROP,
          icon: markerIcon,
        });
        // If the user clicks a hotel marker, show the details of that hotel
        // in an info window.

        markers[i].placeResult = results[i];
        google.maps.event.addListener(markers[i], "click", showInfoWindow);
        setTimeout(dropMarker(i), i * 100);
        addResult(results[i], i);
      }
    }
  });
}

function clearMarkers() {
  for (let i = 0; i < markers.length; i++) {
    if (markers[i]) {
      markers[i].setMap(null);
    }
  }
  markers = [];
}

// Set the country restriction based on user input.
// Also center and zoom the map on the given country.
function setAutocompleteCountry() {
  const country = document.getElementById("country").value;

  if (country == "all") {
    autocomplete.setComponentRestrictions({ country: [] });
    map.setCenter({ lat: 15, lng: 0 });
    map.setZoom(2);
  } else {
    autocomplete.setComponentRestrictions({ country: country });
    map.setCenter(countries[country].center);
    map.setZoom(countries[country].zoom);
  }
  clearResults();
  clearMarkers();
}

function dropMarker(i) {
  return function () {
    markers[i].setMap(map);
  };
}

function addResult(result, i) {
  const results = document.getElementById("results");
  const markerLetter = String.fromCharCode("A".charCodeAt(0) + (i % 26));
  const markerIcon = MARKER_PATH + markerLetter + ".png";
  const tr = document.createElement("tr");
  tr.style.backgroundColor = i % 2 === 0 ? "#F0F0F0" : "#FFFFFF";

  tr.onclick = function () {
    google.maps.event.trigger(markers[i], "click");
  };
  const iconTd = document.createElement("td");
  const nameTd = document.createElement("td");
  const icon = document.createElement("img");
  icon.src = markerIcon;
  icon.setAttribute("class", "placeIcon");
  icon.setAttribute("className", "placeIcon");
  const name = document.createTextNode(result.name);
  iconTd.appendChild(icon);
  nameTd.appendChild(name);
  tr.appendChild(iconTd);
  tr.appendChild(nameTd);
  results.appendChild(tr);
}

function clearResults() {
  const results = document.getElementById("results");

  while (results.childNodes[0]) {
    results.removeChild(results.childNodes[0]);
  }
}

// Get the place details for a hotel. Show the information in an info window,
// anchored on the marker for the hotel that the user selected.
function showInfoWindow() {
  const marker = this;
  places.getDetails(
    { placeId: marker.placeResult.place_id },
    (place, status) => {
      if (status !== google.maps.places.PlacesServiceStatus.OK) {
        return;
      }
      infoWindow.open(map, marker);
      buildIWContent(place);
    }
  );
}

// Load the place information into the HTML elements used by the info window.
function buildIWContent(place) {
  document.getElementById("iw-icon").innerHTML =
    '<img class="hotelIcon" ' + 'src="' + place.icon + '"/>';
  document.getElementById("iw-url").innerHTML =
    '<b><a href="' + place.url + '">' + place.name + "</a></b>";
  document.getElementById("iw-address").textContent = place.vicinity;

  if (place.formatted_phone_number) {
    document.getElementById("iw-phone-row").style.display = "";
    document.getElementById("iw-phone").textContent =
      place.formatted_phone_number;
  } else {
    document.getElementById("iw-phone-row").style.display = "none";
  }

  // Assign a five-star rating to the hotel, using a black star ('&#10029;')
  // to indicate the rating the hotel has earned, and a white star ('&#10025;')
  // for the rating points not achieved.
  if (place.rating) {
    let ratingHtml = "";

    for (let i = 0; i < 5; i++) {
      if (place.rating < i + 0.5) {
        ratingHtml += "&#10025;";
      } else {
        ratingHtml += "&#10029;";
      }
      document.getElementById("iw-rating-row").style.display = "";
      document.getElementById("iw-rating").innerHTML = ratingHtml;
    }
  } else {
    document.getElementById("iw-rating-row").style.display = "none";
  }

  // The regexp isolates the first part of the URL (domain plus subdomain)
  // to give a short URL for displaying in the info window.
  if (place.website) {
    let fullUrl = place.website;
    let website = String(hostnameRegexp.exec(place.website));

    if (!website) {
      website = "http://" + place.website + "/";
      fullUrl = website;
    }
    document.getElementById("iw-website-row").style.display = "";
    document.getElementById("iw-website").textContent = website;
  } else {
    document.getElementById("iw-website-row").style.display = "none";
  }
}

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;
}

body {
  padding: 0 !important;
}

table {
  font-size: 12px;
}

.hotel-search {
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  background: #fff;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  left: 0;
  position: absolute;
  top: 0;
  width: 440px;
  z-index: 1;
}

#map {
  margin-top: 40px;
  width: 440px;
}

#listing {
  position: absolute;
  width: 200px;
  height: 470px;
  overflow: auto;
  left: 442px;
  top: 0px;
  cursor: pointer;
  overflow-x: hidden;
}

#findhotels {
  font-size: 14px;
}

#locationField {
  -webkit-box-flex: 1 1 190px;
  -ms-flex: 1 1 190px;
  flex: 1 1 190px;
  margin: 0 8px;
}

#controls {
  -webkit-box-flex: 1 1 140px;
  -ms-flex: 1 1 140px;
  flex: 1 1 140px;
}

#autocomplete {
  width: 100%;
}

#country {
  width: 100%;
}

.placeIcon {
  width: 20px;
  height: 34px;
  margin: 4px;
}

.hotelIcon {
  width: 24px;
  height: 24px;
}

#resultsTable {
  border-collapse: collapse;
  width: 240px;
}

#rating {
  font-size: 13px;
  font-family: Arial Unicode MS;
}

.iw_table_row {
  height: 18px;
}

.iw_attribute_name {
  font-weight: bold;
  text-align: right;
}

.iw_table_icon {
  text-align: right;
}

HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Place Autocomplete Hotel Search</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=places&v=weekly"
      defer
    ></script>
    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script src="./app.js"></script>
  </head>
  <body>
    <div class="hotel-search">
      <div id="findhotels">Find hotels in:</div>

      <div id="locationField">
        <input id="autocomplete" placeholder="Enter a city" type="text" />
      </div>

      <div id="controls">
        <select id="country">
          <option value="all">All</option>
          <option value="au">Australia</option>
          <option value="br">Brazil</option>
          <option value="ca">Canada</option>
          <option value="fr">France</option>
          <option value="de">Germany</option>
          <option value="mx">Mexico</option>
          <option value="nz">New Zealand</option>
          <option value="it">Italy</option>
          <option value="za">South Africa</option>
          <option value="es">Spain</option>
          <option value="pt">Portugal</option>
          <option value="us" selected>U.S.A.</option>
          <option value="uk">United Kingdom</option>
        </select>
      </div>
    </div>

    <div id="map"></div>

    <div id="listing">
      <table id="resultsTable">
        <tbody id="results"></tbody>
      </table>
    </div>

    <div style="display: none">
      <div id="info-content">
        <table>
          <tr id="iw-url-row" class="iw_table_row">
            <td id="iw-icon" class="iw_table_icon"></td>
            <td id="iw-url"></td>
          </tr>
          <tr id="iw-address-row" class="iw_table_row">
            <td class="iw_attribute_name">Address:</td>
            <td id="iw-address"></td>
          </tr>
          <tr id="iw-phone-row" class="iw_table_row">
            <td class="iw_attribute_name">Telephone:</td>
            <td id="iw-phone"></td>
          </tr>
          <tr id="iw-rating-row" class="iw_table_row">
            <td class="iw_attribute_name">Rating:</td>
            <td id="iw-rating"></td>
          </tr>
          <tr id="iw-website-row" class="iw_table_row">
            <td class="iw_attribute_name">Website:</td>
            <td id="iw-website"></td>
          </tr>
        </table>
      </div>
    </div>
  </body>
</html>

All

<!DOCTYPE html>
<html>
  <head>
    <title>Place Autocomplete Hotel Search</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap&libraries=places&v=weekly"
      defer
    ></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;
      }

      body {
        padding: 0 !important;
      }

      table {
        font-size: 12px;
      }

      .hotel-search {
        -webkit-box-align: center;
        -ms-flex-align: center;
        align-items: center;
        background: #fff;
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex;
        left: 0;
        position: absolute;
        top: 0;
        width: 440px;
        z-index: 1;
      }

      #map {
        margin-top: 40px;
        width: 440px;
      }

      #listing {
        position: absolute;
        width: 200px;
        height: 470px;
        overflow: auto;
        left: 442px;
        top: 0px;
        cursor: pointer;
        overflow-x: hidden;
      }

      #findhotels {
        font-size: 14px;
      }

      #locationField {
        -webkit-box-flex: 1 1 190px;
        -ms-flex: 1 1 190px;
        flex: 1 1 190px;
        margin: 0 8px;
      }

      #controls {
        -webkit-box-flex: 1 1 140px;
        -ms-flex: 1 1 140px;
        flex: 1 1 140px;
      }

      #autocomplete {
        width: 100%;
      }

      #country {
        width: 100%;
      }

      .placeIcon {
        width: 20px;
        height: 34px;
        margin: 4px;
      }

      .hotelIcon {
        width: 24px;
        height: 24px;
      }

      #resultsTable {
        border-collapse: collapse;
        width: 240px;
      }

      #rating {
        font-size: 13px;
        font-family: Arial Unicode MS;
      }

      .iw_table_row {
        height: 18px;
      }

      .iw_attribute_name {
        font-weight: bold;
        text-align: right;
      }

      .iw_table_icon {
        text-align: right;
      }
    </style>
    <script>
      "use strict";

      // This example uses the autocomplete feature of the Google Places API.
      // It allows the user to find all hotels in a given place, within a given
      // country. It then displays markers for all the hotels returned,
      // with on-click details for each hotel.
      // This example requires the Places library. Include the libraries=places
      // parameter when you first load the API. For example:
      // <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
      let map;
      let places;
      let infoWindow;
      let markers = [];
      let autocomplete;
      const countryRestrict = {
        country: "us",
      };
      const MARKER_PATH =
        "https://developers.google.com/maps/documentation/javascript/images/marker_green";
      const hostnameRegexp = new RegExp("^https?://.+?/");
      const countries = {
        au: {
          center: {
            lat: -25.3,
            lng: 133.8,
          },
          zoom: 4,
        },
        br: {
          center: {
            lat: -14.2,
            lng: -51.9,
          },
          zoom: 3,
        },
        ca: {
          center: {
            lat: 62,
            lng: -110.0,
          },
          zoom: 3,
        },
        fr: {
          center: {
            lat: 46.2,
            lng: 2.2,
          },
          zoom: 5,
        },
        de: {
          center: {
            lat: 51.2,
            lng: 10.4,
          },
          zoom: 5,
        },
        mx: {
          center: {
            lat: 23.6,
            lng: -102.5,
          },
          zoom: 4,
        },
        nz: {
          center: {
            lat: -40.9,
            lng: 174.9,
          },
          zoom: 5,
        },
        it: {
          center: {
            lat: 41.9,
            lng: 12.6,
          },
          zoom: 5,
        },
        za: {
          center: {
            lat: -30.6,
            lng: 22.9,
          },
          zoom: 5,
        },
        es: {
          center: {
            lat: 40.5,
            lng: -3.7,
          },
          zoom: 5,
        },
        pt: {
          center: {
            lat: 39.4,
            lng: -8.2,
          },
          zoom: 6,
        },
        us: {
          center: {
            lat: 37.1,
            lng: -95.7,
          },
          zoom: 3,
        },
        uk: {
          center: {
            lat: 54.8,
            lng: -4.6,
          },
          zoom: 5,
        },
      };

      function initMap() {
        map = new google.maps.Map(document.getElementById("map"), {
          zoom: countries["us"].zoom,
          center: countries["us"].center,
          mapTypeControl: false,
          panControl: false,
          zoomControl: false,
          streetViewControl: false,
        });
        infoWindow = new google.maps.InfoWindow({
          content: document.getElementById("info-content"),
        }); // Create the autocomplete object and associate it with the UI input control.
        // Restrict the search to the default country, and to place type "cities".

        autocomplete = new google.maps.places.Autocomplete(
          document.getElementById("autocomplete"),
          {
            types: ["(cities)"],
            componentRestrictions: countryRestrict,
          }
        );
        places = new google.maps.places.PlacesService(map);
        autocomplete.addListener("place_changed", onPlaceChanged); // Add a DOM event listener to react when the user selects a country.

        document
          .getElementById("country")
          .addEventListener("change", setAutocompleteCountry);
      } // When the user selects a city, get the place details for the city and
      // zoom the map in on the city.

      function onPlaceChanged() {
        const place = autocomplete.getPlace();

        if (place.geometry) {
          map.panTo(place.geometry.location);
          map.setZoom(15);
          search();
        } else {
          document.getElementById("autocomplete").placeholder = "Enter a city";
        }
      } // Search for hotels in the selected city, within the viewport of the map.

      function search() {
        const search = {
          bounds: map.getBounds(),
          types: ["lodging"],
        };
        places.nearbySearch(search, (results, status, pagination) => {
          if (status === google.maps.places.PlacesServiceStatus.OK) {
            clearResults();
            clearMarkers(); // Create a marker for each hotel found, and
            // assign a letter of the alphabetic to each marker icon.

            for (let i = 0; i < results.length; i++) {
              const markerLetter = String.fromCharCode(
                "A".charCodeAt(0) + (i % 26)
              );
              const markerIcon = MARKER_PATH + markerLetter + ".png"; // Use marker animation to drop the icons incrementally on the map.

              markers[i] = new google.maps.Marker({
                position: results[i].geometry.location,
                animation: google.maps.Animation.DROP,
                icon: markerIcon,
              }); // If the user clicks a hotel marker, show the details of that hotel
              // in an info window.

              markers[i].placeResult = results[i];
              google.maps.event.addListener(
                markers[i],
                "click",
                showInfoWindow
              );
              setTimeout(dropMarker(i), i * 100);
              addResult(results[i], i);
            }
          }
        });
      }

      function clearMarkers() {
        for (let i = 0; i < markers.length; i++) {
          if (markers[i]) {
            markers[i].setMap(null);
          }
        }

        markers = [];
      } // Set the country restriction based on user input.
      // Also center and zoom the map on the given country.

      function setAutocompleteCountry() {
        const country = document.getElementById("country").value;

        if (country == "all") {
          autocomplete.setComponentRestrictions({
            country: [],
          });
          map.setCenter({
            lat: 15,
            lng: 0,
          });
          map.setZoom(2);
        } else {
          autocomplete.setComponentRestrictions({
            country: country,
          });
          map.setCenter(countries[country].center);
          map.setZoom(countries[country].zoom);
        }

        clearResults();
        clearMarkers();
      }

      function dropMarker(i) {
        return function () {
          markers[i].setMap(map);
        };
      }

      function addResult(result, i) {
        const results = document.getElementById("results");
        const markerLetter = String.fromCharCode("A".charCodeAt(0) + (i % 26));
        const markerIcon = MARKER_PATH + markerLetter + ".png";
        const tr = document.createElement("tr");
        tr.style.backgroundColor = i % 2 === 0 ? "#F0F0F0" : "#FFFFFF";

        tr.onclick = function () {
          google.maps.event.trigger(markers[i], "click");
        };

        const iconTd = document.createElement("td");
        const nameTd = document.createElement("td");
        const icon = document.createElement("img");
        icon.src = markerIcon;
        icon.setAttribute("class", "placeIcon");
        icon.setAttribute("className", "placeIcon");
        const name = document.createTextNode(result.name);
        iconTd.appendChild(icon);
        nameTd.appendChild(name);
        tr.appendChild(iconTd);
        tr.appendChild(nameTd);
        results.appendChild(tr);
      }

      function clearResults() {
        const results = document.getElementById("results");

        while (results.childNodes[0]) {
          results.removeChild(results.childNodes[0]);
        }
      } // Get the place details for a hotel. Show the information in an info window,
      // anchored on the marker for the hotel that the user selected.

      function showInfoWindow() {
        const marker = this;
        places.getDetails(
          {
            placeId: marker.placeResult.place_id,
          },
          (place, status) => {
            if (status !== google.maps.places.PlacesServiceStatus.OK) {
              return;
            }

            infoWindow.open(map, marker);
            buildIWContent(place);
          }
        );
      } // Load the place information into the HTML elements used by the info window.

      function buildIWContent(place) {
        document.getElementById("iw-icon").innerHTML =
          '<img class="hotelIcon" ' + 'src="' + place.icon + '"/>';
        document.getElementById("iw-url").innerHTML =
          '<b><a href="' + place.url + '">' + place.name + "</a></b>";
        document.getElementById("iw-address").textContent = place.vicinity;

        if (place.formatted_phone_number) {
          document.getElementById("iw-phone-row").style.display = "";
          document.getElementById("iw-phone").textContent =
            place.formatted_phone_number;
        } else {
          document.getElementById("iw-phone-row").style.display = "none";
        } // Assign a five-star rating to the hotel, using a black star ('&#10029;')
        // to indicate the rating the hotel has earned, and a white star ('&#10025;')
        // for the rating points not achieved.

        if (place.rating) {
          let ratingHtml = "";

          for (let i = 0; i < 5; i++) {
            if (place.rating < i + 0.5) {
              ratingHtml += "&#10025;";
            } else {
              ratingHtml += "&#10029;";
            }

            document.getElementById("iw-rating-row").style.display = "";
            document.getElementById("iw-rating").innerHTML = ratingHtml;
          }
        } else {
          document.getElementById("iw-rating-row").style.display = "none";
        } // The regexp isolates the first part of the URL (domain plus subdomain)
        // to give a short URL for displaying in the info window.

        if (place.website) {
          let fullUrl = place.website;
          let website = String(hostnameRegexp.exec(place.website));

          if (!website) {
            website = "http://" + place.website + "/";
            fullUrl = website;
          }

          document.getElementById("iw-website-row").style.display = "";
          document.getElementById("iw-website").textContent = website;
        } else {
          document.getElementById("iw-website-row").style.display = "none";
        }
      }
    </script>
  </head>
  <body>
    <div class="hotel-search">
      <div id="findhotels">Find hotels in:</div>

      <div id="locationField">
        <input id="autocomplete" placeholder="Enter a city" type="text" />
      </div>

      <div id="controls">
        <select id="country">
          <option value="all">All</option>
          <option value="au">Australia</option>
          <option value="br">Brazil</option>
          <option value="ca">Canada</option>
          <option value="fr">France</option>
          <option value="de">Germany</option>
          <option value="mx">Mexico</option>
          <option value="nz">New Zealand</option>
          <option value="it">Italy</option>
          <option value="za">South Africa</option>
          <option value="es">Spain</option>
          <option value="pt">Portugal</option>
          <option value="us" selected>U.S.A.</option>
          <option value="uk">United Kingdom</option>
        </select>
      </div>
    </div>

    <div id="map"></div>

    <div id="listing">
      <table id="resultsTable">
        <tbody id="results"></tbody>
      </table>
    </div>

    <div style="display: none">
      <div id="info-content">
        <table>
          <tr id="iw-url-row" class="iw_table_row">
            <td id="iw-icon" class="iw_table_icon"></td>
            <td id="iw-url"></td>
          </tr>
          <tr id="iw-address-row" class="iw_table_row">
            <td class="iw_attribute_name">Address:</td>
            <td id="iw-address"></td>
          </tr>
          <tr id="iw-phone-row" class="iw_table_row">
            <td class="iw_attribute_name">Telephone:</td>
            <td id="iw-phone"></td>
          </tr>
          <tr id="iw-rating-row" class="iw_table_row">
            <td class="iw_attribute_name">Rating:</td>
            <td id="iw-rating"></td>
          </tr>
          <tr id="iw-website-row" class="iw_table_row">
            <td class="iw_attribute_name">Website:</td>
            <td id="iw-website"></td>
          </tr>
        </table>
      </div>
    </div>
  </body>
</html>
"use strict"; // This example uses the autocomplete feature of the Google Places API. // It allows the user to find all hotels in a given place, within a given // country. It then displays markers for all the hotels returned, // with on-click details for each hotel. // This example requires the Places library. Include the libraries=places // parameter when you first load the API. For example: // <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBIwzALxUPNbatRBj3Xi1Uhp0fFzwWNBkE&libraries=places"> let map; let places; let infoWindow; let markers = []; let autocomplete; const countryRestrict = { country: "us", }; const MARKER_PATH = "https://developers.google.com/maps/documentation/javascript/images/marker_green"; const hostnameRegexp = new RegExp("^https?://.+?/"); const countries = { au: { center: { lat: -25.3, lng: 133.8, }, zoom: 4, }, br: { center: { lat: -14.2, lng: -51.9, }, zoom: 3, }, ca: { center: { lat: 62, lng: -110.0, }, zoom: 3, }, fr: { center: { lat: 46.2, lng: 2.2, }, zoom: 5, }, de: { center: { lat: 51.2, lng: 10.4, }, zoom: 5, }, mx: { center: { lat: 23.6, lng: -102.5, }, zoom: 4, }, nz: { center: { lat: -40.9, lng: 174.9, }, zoom: 5, }, it: { center: { lat: 41.9, lng: 12.6, }, zoom: 5, }, za: { center: { lat: -30.6, lng: 22.9, }, zoom: 5, }, es: { center: { lat: 40.5, lng: -3.7, }, zoom: 5, }, pt: { center: { lat: 39.4, lng: -8.2, }, zoom: 6, }, us: { center: { lat: 37.1, lng: -95.7, }, zoom: 3, }, uk: { center: { lat: 54.8, lng: -4.6, }, zoom: 5, }, }; function initMap() { map = new google.maps.Map(document.getElementById("map"), { zoom: countries["us"].zoom, center: countries["us"].center, mapTypeControl: false, panControl: false, zoomControl: false, streetViewControl: false, }); infoWindow = new google.maps.InfoWindow({ content: document.getElementById("info-content"), }); // Create the autocomplete object and associate it with the UI input control. // Restrict the search to the default country, and to place type "cities". autocomplete = new google.maps.places.Autocomplete( document.getElementById("autocomplete"), { types: ["(cities)"], componentRestrictions: countryRestrict, } ); places = new google.maps.places.PlacesService(map); autocomplete.addListener("place_changed", onPlaceChanged); // Add a DOM event listener to react when the user selects a country. document .getElementById("country") .addEventListener("change", setAutocompleteCountry); } // When the user selects a city, get the place details for the city and // zoom the map in on the city. function onPlaceChanged() { const place = autocomplete.getPlace(); if (place.geometry) { map.panTo(place.geometry.location); map.setZoom(15); search(); } else { document.getElementById("autocomplete").placeholder = "Enter a city"; } } // Search for hotels in the selected city, within the viewport of the map. function search() { const search = { bounds: map.getBounds(), types: ["lodging"], }; places.nearbySearch(search, (results, status, pagination) => { if (status === google.maps.places.PlacesServiceStatus.OK) { clearResults(); clearMarkers(); // Create a marker for each hotel found, and // assign a letter of the alphabetic to each marker icon. for (let i = 0; i < results.length; i++) { const markerLetter = String.fromCharCode("A".charCodeAt(0) + (i % 26)); const markerIcon = MARKER_PATH + markerLetter + ".png"; // Use marker animation to drop the icons incrementally on the map. markers[i] = new google.maps.Marker({ position: results[i].geometry.location, animation: google.maps.Animation.DROP, icon: markerIcon, }); // If the user clicks a hotel marker, show the details of that hotel // in an info window. markers[i].placeResult = results[i]; google.maps.event.addListener(markers[i], "click", showInfoWindow); setTimeout(dropMarker(i), i * 100); addResult(results[i], i); } } }); } function clearMarkers() { for (let i = 0; i < markers.length; i++) { if (markers[i]) { markers[i].setMap(null); } } markers = []; } // Set the country restriction based on user input. // Also center and zoom the map on the given country. function setAutocompleteCountry() { const country = document.getElementById("country").value; if (country == "all") { autocomplete.setComponentRestrictions({ country: [], }); map.setCenter({ lat: 15, lng: 0, }); map.setZoom(2); } else { autocomplete.setComponentRestrictions({ country: country, }); map.setCenter(countries[country].center); map.setZoom(countries[country].zoom); } clearResults(); clearMarkers(); } function dropMarker(i) { return function () { markers[i].setMap(map); }; } function addResult(result, i) { const results = document.getElementById("results"); const markerLetter = String.fromCharCode("A".charCodeAt(0) + (i % 26)); const markerIcon = MARKER_PATH + markerLetter + ".png"; const tr = document.createElement("tr"); tr.style.backgroundColor = i % 2 === 0 ? "#F0F0F0" : "#FFFFFF"; tr.onclick = function () { google.maps.event.trigger(markers[i], "click"); }; const iconTd = document.createElement("td"); const nameTd = document.createElement("td"); const icon = document.createElement("img"); icon.src = markerIcon; icon.setAttribute("class", "placeIcon"); icon.setAttribute("className", "placeIcon"); const name = document.createTextNode(result.name); iconTd.appendChild(icon); nameTd.appendChild(name); tr.appendChild(iconTd); tr.appendChild(nameTd); results.appendChild(tr); } function clearResults() { const results = document.getElementById("results"); while (results.childNodes[0]) { results.removeChild(results.childNodes[0]); } } // Get the place details for a hotel. Show the information in an info window, // anchored on the marker for the hotel that the user selected. function showInfoWindow() { const marker = this; places.getDetails( { placeId: marker.placeResult.place_id, }, (place, status) => { if (status !== google.maps.places.PlacesServiceStatus.OK) { return; } infoWindow.open(map, marker); buildIWContent(place); } ); } // Load the place information into the HTML elements used by the info window. function buildIWContent(place) { document.getElementById("iw-icon").innerHTML = '<img class="hotelIcon" ' + 'src="' + place.icon + '"/>'; document.getElementById("iw-url").innerHTML = '<b><a href="' + place.url + '">' + place.name + "</a></b>"; document.getElementById("iw-address").textContent = place.vicinity; if (place.formatted_phone_number) { document.getElementById("iw-phone-row").style.display = ""; document.getElementById("iw-phone").textContent = place.formatted_phone_number; } else { document.getElementById("iw-phone-row").style.display = "none"; } // Assign a five-star rating to the hotel, using a black star ('&#10029;') // to indicate the rating the hotel has earned, and a white star ('&#10025;') // for the rating points not achieved. if (place.rating) { let ratingHtml = ""; for (let i = 0; i < 5; i++) { if (place.rating < i + 0.5) { ratingHtml += "&#10025;"; } else { ratingHtml += "&#10029;"; } document.getElementById("iw-rating-row").style.display = ""; document.getElementById("iw-rating").innerHTML = ratingHtml; } } else { document.getElementById("iw-rating-row").style.display = "none"; } // The regexp isolates the first part of the URL (domain plus subdomain) // to give a short URL for displaying in the info window. if (place.website) { let fullUrl = place.website; let website = String(hostnameRegexp.exec(place.website)); if (!website) { website = "http://" + place.website + "/"; fullUrl = website; } document.getElementById("iw-website-row").style.display = ""; document.getElementById("iw-website").textContent = website; } else { document.getElementById("iw-website-row").style.display = "none"; } }
/* 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; } body { padding: 0 !important; } table { font-size: 12px; } .hotel-search { -webkit-box-align: center; -ms-flex-align: center; align-items: center; background: #fff; display: -webkit-box; display: -ms-flexbox; display: flex; left: 0; position: absolute; top: 0; width: 440px; z-index: 1; } #map { margin-top: 40px; width: 440px; } #listing { position: absolute; width: 200px; height: 470px; overflow: auto; left: 442px; top: 0px; cursor: pointer; overflow-x: hidden; } #findhotels { font-size: 14px; } #locationField { -webkit-box-flex: 1 1 190px; -ms-flex: 1 1 190px; flex: 1 1 190px; margin: 0 8px; } #controls { -webkit-box-flex: 1 1 140px; -ms-flex: 1 1 140px; flex: 1 1 140px; } #autocomplete { width: 100%; } #country { width: 100%; } .placeIcon { width: 20px; height: 34px; margin: 4px; } .hotelIcon { width: 24px; height: 24px; } #resultsTable { border-collapse: collapse; width: 240px; } #rating { font-size: 13px; font-family: Arial Unicode MS; } .iw_table_row { height: 18px; } .iw_attribute_name { font-weight: bold; text-align: right; } .iw_table_icon { text-align: right; }
<!DOCTYPE html> <html> <head> <title>Place Autocomplete Hotel Search</title> <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBIwzALxUPNbatRBj3Xi1Uhp0fFzwWNBkE&callback=initMap&libraries=places&v=weekly" defer ></script> <!-- jsFiddle will insert css and js --> </head> <body> <div class="hotel-search"> <div id="findhotels">Find hotels in:</div> <div id="locationField"> <input id="autocomplete" placeholder="Enter a city" type="text" /> </div> <div id="controls"> <select id="country"> <option value="all">All</option> <option value="au">Australia</option> <option value="br">Brazil</option> <option value="ca">Canada</option> <option value="fr">France</option> <option value="de">Germany</option> <option value="mx">Mexico</option> <option value="nz">New Zealand</option> <option value="it">Italy</option> <option value="za">South Africa</option> <option value="es">Spain</option> <option value="pt">Portugal</option> <option value="us" selected>U.S.A.</option> <option value="uk">United Kingdom</option> </select> </div> </div> <div id="map"></div> <div id="listing"> <table id="resultsTable"> <tbody id="results"></tbody> </table> </div> <div style="display: none"> <div id="info-content"> <table> <tr id="iw-url-row" class="iw_table_row"> <td id="iw-icon" class="iw_table_icon"></td> <td id="iw-url"></td> </tr> <tr id="iw-address-row" class="iw_table_row"> <td class="iw_attribute_name">Address:</td> <td id="iw-address"></td> </tr> <tr id="iw-phone-row" class="iw_table_row"> <td class="iw_attribute_name">Telephone:</td> <td id="iw-phone"></td> </tr> <tr id="iw-rating-row" class="iw_table_row"> <td class="iw_attribute_name">Rating:</td> <td id="iw-rating"></td> </tr> <tr id="iw-website-row" class="iw_table_row"> <td class="iw_attribute_name">Website:</td> <td id="iw-website"></td> </tr> </table> </div> </div> </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.

npm i -g '@googlemaps/js-samples'
googlemaps-js-samples init places-autocomplete-hotelsearch 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