Michael Millar
Michael Millar

Reputation: 1604

Throttling or delaying Google Places API autosuggestions

Google Places API Web Service has a 1000 request per day quota limit for non-billed accounts. When using the search box autosuggestion feature providing multiple places for each key press, this limit will be reached fairly quickly.

<input id="pac-input" class="controls" type="text" placeholder="Search Box">
<div style="height:100%; width:100%;position:absolute;">
<div id="map"></div>
</div>
<script>
  window.onload = initAutocomplete;
  function initAutocomplete() {

            var my_position = new google.maps.LatLng(51.163375, 10.447683);
            var map = new google.maps.Map(document.getElementById('map'), {
              center: {lat: 51.163375, lng: 10.447683},
              disableDoubleClickZoom: true,
              zoom: 9,
              mapTypeId: 'roadmap'
            });

            // Create the search box and link it to the UI element.
            var input = document.getElementById('pac-input');

            var searchBox = new google.maps.places.SearchBox(input);
            map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);

            // Bias the SearchBox results towards current map's viewport.
            map.addListener('bounds_changed', function() {
              searchBox.setBounds(map.getBounds());
            });

            var markers = [];
            var crowdMarker = new google.maps.Marker({
              position: my_position,
              map: map
            });
            google.maps.event.addListener(map, 'dblclick', function(e){
              var positionDoubleClick = e.latLng;
              crowdMarker.setPosition(positionDoubleClick);
              var lat = crowdMarker.getPosition().lat();
              var lng = crowdMarker.getPosition().lng();
            });
            // Listen for the event fired when the user selects a prediction and retrieve
            // more details for that place.

            google.maps.event.addDomListener(searchBox, 'keydown', function (e) {
              if (e.keyCode == 13) {
                  e.preventDefault();
              }
          });
            searchBox.addListener('places_changed', function() {
              var places = searchBox.getPlaces();

              if (places.length == 0) {
                return;
              }

              // Clear out the old markers.
              markers.forEach(function(marker) {
                marker.setMap(null);
              });
              markers = [];

              // For each place, get the icon, name and location.
              var bounds = new google.maps.LatLngBounds();
              places.forEach(function(place) {
                if (!place.geometry) {
                  console.log("Returned place contains no geometry");
                  return;
                }
                var icon = {
                  url: place.icon,
                  size: new google.maps.Size(71, 71),
                  origin: new google.maps.Point(0, 0),
                  anchor: new google.maps.Point(17, 34),
                  scaledSize: new google.maps.Size(25, 25)
                };

                // Create a marker for each place.
                markers.push(new google.maps.Marker({
                  map: map,
                  icon: icon,
                  title: place.name,
                  position: place.geometry.location
                }));

                if (place.geometry.viewport) {
                  // Only geocodes have viewport.
                  bounds.union(place.geometry.viewport);
                } else {
                  bounds.extend(place.geometry.location);
                }
              });
              map.fitBounds(bounds);
            });
          }
    </script>
    <script src="https://maps.googleapis.com/maps/api/js?key=myAPIKey&libraries=places"
    async defer></script>

Things I've tried include setting a bound to the SearchBox to limit the suggestions to a set area, as well as adding a delay within the keydown section.

Can anyone suggest a way to be able to throttle or delay the autosuggestion appearing, and therefore sending fewer requests?

Upvotes: 6

Views: 3723

Answers (2)

Kevin Beal
Kevin Beal

Reputation: 10858

You can monkey-patch the input event on the input element to intercept it and throttle, debounce, or stop it there.

Below is an example of throttling, but the idea can easily be extended to fit your use case:

function getPlacesFromInput(input, fn) {
    // patching happens here
    const restore = throttleEventListener("input", input, 750);
    const api = new google.maps.places.Autocomplete(input, {
        /* your options */
    });
    const listener = api.addListener("place_changed", function () {
        fn(api.getPlace()); // place selected
    });
    return function _cleanUp() {
        restore();
        listener.remove();
    };
}

function throttleEventListener(eventType, element, time) {
    // Keep the original method to be un-patched later, if desired
    const originalAddEventListener = element.addEventListener;
    element.addEventListener = function (type, listener, options) {
        if (type === eventType) {
            // You can intercept it here:
            const debouncedListener = _.throttle(listener, time);
            originalAddEventListener.call(
                this,
                type,
                debouncedListener,
                options
            );
        } else {
            // Every other event is treated as normal this way
            originalAddEventListener.call(this, type, listener, options);
        }
    };
    // Returns a function with which we can undo the patching
    return function _cleanUp() {
        element.addEventListener = originalAddEventListener;
    };
}

Beware of using the this keyword when patching these functions. You can learn how to do it properly here: https://stackoverflow.com/a/20279485/832447

Upvotes: 1

xomena
xomena

Reputation: 32178

I'm afraid the current implementation of autocomplete and search box in places library of Maps JavaScript API doesn't allow to throttle or delay requests programmatically. You can see a feature request in Google issue tracker for this:

https://issuetracker.google.com/issues/35823678

Please star this feature request to add your vote. The only workaround I can think of is implementing your own autocomplete element that uses a google.maps.places.AutocompleteService class and where you will be able to control a frequency of requests sent to AutocompleteService.

https://developers.google.com/maps/documentation/javascript/reference#AutocompleteService

Upvotes: 2

Related Questions