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 && place.geometry.location) {
    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[] | null,
      status: google.maps.places.PlacesServiceStatus,
      pagination: google.maps.places.PlaceSearchPagination | null
    ) => {
      if (status === google.maps.places.PlacesServiceStatus.OK && results) {
        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 && place.geometry.location) {
    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 && results) {
      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>
    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script src="./index.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>

    <!-- 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>Place Autocomplete Hotel Search</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;
      }

      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>
      // 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 && place.geometry.location) {
          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 && results) {
            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>

    <!-- 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>
// 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=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&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 && place.geometry.location) { 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 && results) { 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> <!-- 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> <!-- Async script executes immediately and must be after any DOM elements used in callback. --> <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&libraries=places&v=weekly" async ></script> </body> </html>