37

I'm using a Google Places Autocomplete and I simply want it to select the top item in the results list when the enter key is pressed in the form field and suggestions exist. I know this has been asked before:

Google maps Places API V3 autocomplete - select first option on enter

Google maps Places API V3 autocomplete - select first option on enter (and have it stay that way)

But the answers in those questions don't seem to actually work, or they address specific added functionality.

It also seems like something like the following should work (but it doesn't):

$("input#autocomplete").keydown(function(e) {
  if (e.which == 13) {          
    //if there are suggestions...
    if ($(".pac-container .pac-item").length) {
      //click on the first item in the list or simulate a down arrow key event
      //it does get this far, but I can't find a way to actually select the item
      $(".pac-container .pac-item:first").click();
    } else {
      //there are no suggestions
    }
  }
});

Any suggestions would be greatly appreciated!

Community
  • 1
  • 1
NChase
  • 1,638
  • 4
  • 22
  • 25
  • 2
    You'd have to use the google.maps.places.AutocompleteService class and read the AutocompletePrediction manually: https://developers.google.com/maps/documentation/javascript/reference#AutocompleteService – Marcelo Jan 30 '13 at 11:08
  • 6
    @Mubix accepted answer in the second question you linked to covers what seems to be needed, with the addition of some extra functionality that can be easily stripped out. As per this demo I just forked from the above doing just that: http://jsfiddle.net/Ut2U4/1/ – Craig Blagg Jan 30 '13 at 11:42
  • 1
    @CraigBlagg Thanks! I got that to work eventually. Not sure why it didn't work the first time around, but rebuilding it line-by-line got the desired result! – NChase Feb 01 '13 at 09:53
  • Great. Strange it didn't work straight away (seems to work here). But glad it worked out @NChase – Craig Blagg Feb 01 '13 at 10:27
  • @CraigBlagg, in your sample (jsfiddle.net/Ut2U4/1), it's needed to make sure 'keypress' event is removed from the document when 'focusout' is fired (line 14). Otherwise, you're attaching it to the document every time input is getting focus. – Nik Sumeiko Mar 25 '13 at 18:28

9 Answers9

23

I've read the many answers of this question, and of the linked questions' answers so many times, before finding that the best answer is this one (Nota: sadly, it's not the accepted answer!).

I've modified 2 or 3 lines to turn it into a ready-to-use function that you can copy/paste in your code and apply to many input elements if needed. Here it is:

var selectFirstOnEnter = function(input) {  // store the original event binding function
    var _addEventListener = (input.addEventListener) ? input.addEventListener : input.attachEvent;
    function addEventListenerWrapper(type, listener) {  // Simulate a 'down arrow' keypress on hitting 'return' when no pac suggestion is selected, and then trigger the original listener.
        if (type == "keydown") { 
            var orig_listener = listener;
            listener = function(event) {
                var suggestion_selected = $(".pac-item-selected").length > 0;
                if (event.which == 13 && !suggestion_selected) { 
                    var simulated_downarrow = $.Event("keydown", {keyCode: 40, which: 40}); 
                    orig_listener.apply(input, [simulated_downarrow]); 
                }
                orig_listener.apply(input, [event]);
            };
        }
        _addEventListener.apply(input, [type, listener]); // add the modified listener
    }
    if (input.addEventListener) { 
        input.addEventListener = addEventListenerWrapper; 
    } else if (input.attachEvent) { 
        input.attachEvent = addEventListenerWrapper; 
    }
}

Usage:

selectFirstOnEnter(input1);
selectFirstOnEnter(input2);
...
Basj
  • 41,386
  • 99
  • 383
  • 673
  • 1
    This works like a charm. It requires jQuery. If you're on Angular like me, here's a little guide on how to install it: https://medium.com/@swarnakishore/how-to-include-and-use-jquery-in-angular-cli-project-592e0fe63176 – Mladen Jun 21 '18 at 11:28
  • 1
    also be sure that you call the function before `var autocomplete = new google.maps.places.Autocomplete(input);` otherwise it won't work – woodii Aug 06 '18 at 08:43
  • 1
    Good answer, terribly formatted code. Thanks anyway :) – Derenir Mar 20 '19 at 14:34
  • 1
    @Derenir fixed! – Basj May 29 '20 at 16:08
9

I am reposting my answer from Google maps Places API V3 autocomplete - select first option on enter:

It seems there is a much better and clean solution: To use google.maps.places.SearchBox instead of google.maps.places.Autocomplete. A code is almost the same, just getting the first from multiple places. On pressing the Enter the the correct list is returned - so it runs out of the box and there is no need for hacks.

See the example HTML page:

http://rawgithub.com/klokan/8408394/raw/5ab795fb36c67ad73c215269f61c7648633ae53e/places-enter-first-item.html

The relevant code snippet is:

var searchBox = new google.maps.places.SearchBox(document.getElementById('searchinput'));

google.maps.event.addListener(searchBox, 'places_changed', function() {
  var place = searchBox.getPlaces()[0];

  if (!place.geometry) return;

  if (place.geometry.viewport) {
    map.fitBounds(place.geometry.viewport);
  } else {
    map.setCenter(place.geometry.location);
    map.setZoom(16);
  }
});

The complete source code of the example is at: https://gist.github.com/klokan/8408394

Community
  • 1
  • 1
MapTiler
  • 1,754
  • 14
  • 22
  • 19
    SeachBox doesn't let you filter by (region) or (cities), or any type, for that matter. – metsfan May 27 '14 at 23:08
  • I tested this, and it doesn't work as expected. The SearchBox does an "autocomplete" request when you type, but when you press enter, it does a request at https://maps.googleapis.com/maps/api/js/PlaceService.QueryPlaces, using the string you entered. This may or may not return actual results. For example, by typing "naxo", the autocomplete list returns as a first result "Naxos ke Mikres Kyklades" (maybe this is based on locale?). Pressing enter, without selecting a list item, returns 0 results. – pkExec Nov 21 '14 at 11:21
  • Search box is so convenient. I really wish it would let me restrict results to geocode or addresses. – Adam Starrh Sep 14 '15 at 13:20
  • 1
    but search box is different from autocomplete. it's more for things like "coffee in new york" – user151496 Feb 26 '16 at 16:47
  • I've been using Autocomplete for about a year now, and after reading this and switching to SearchBox, everything is so much better. I had a special "getPlaceFromAutocompleteList" function as a work around, but this fixes several frustrating edge case bugs. Thank you! – Kelderic May 23 '17 at 14:41
  • Doesn't work anymore. Is there a new way to do this? – theBugger Apr 16 '18 at 17:55
  • I strongly recommend you to choose the `AutocompleteService` instead of `SearchBox` widget. Especially if price matter for you. See this blog article to help you understand the differences: https://blog.woosmap.com/implement-and-optimize-autocomplete-with-google-places-api – Gael Nov 05 '19 at 14:01
6

In my site to achieve this same functionality I neeeded the jQuery simulate plugin (https://github.com/jquery/jquery-simulate) and then attach the event:

$("input#autocomplete").focusin(function () {
    $(document).keypress(function (e) {
        if (e.which == 13) {
            $("input#autocomplete").trigger('focus');
            $("input#autocomplete").simulate('keydown', { keyCode: $.ui.keyCode.DOWN } ).simulate('keydown', { keyCode: $.ui.keyCode.ENTER });
        }
    });
});

The plugin will simulate the action of press the key DOWN and then ENTER, ENTER itself does not work and I couldn't find another way to select the first option.

Hope this helps

Mangiucugna
  • 1,732
  • 1
  • 14
  • 23
  • 1
    This doesn't work in situations where you navigate with your arrow-keys and select it by pressing the enter key. – dnlmzw Feb 26 '15 at 15:26
  • @dnlmzw if you navigate with arrow keys and select it with enter then you get "place_change" fired... – user151496 Feb 26 '16 at 16:48
  • @user151496 that's true, but it will not work if your field is a `
    ` field, then you should do `e.preventDefault();` on enter (`13/$.ui.keyCode.ENTER`)`keydown`, if you don't, the request's response probably won't make it back in time :)
    – jave.web Jun 19 '16 at 21:52
  • This worked perfectly for me after hours of searching and trying myself to hack similar solutions. – Antony D'Andrea Jul 24 '18 at 10:14
6

Working Solution that listens to if the user has started to navigate down the list with the keyboard rather than triggering the false navigation each time

https://codepen.io/callam/pen/RgzxZB

Here are the important bits

// search input
const searchInput = document.getElementById('js-search-input');

// Google Maps autocomplete
const autocomplete = new google.maps.places.Autocomplete(searchInput);

// Has user pressed the down key to navigate autocomplete options?
let hasDownBeenPressed = false;

// Listener outside to stop nested loop returning odd results
searchInput.addEventListener('keydown', (e) => {
    if (e.keyCode === 40) {
        hasDownBeenPressed = true;
    }
});

// GoogleMaps API custom eventlistener method
google.maps.event.addDomListener(searchInput, 'keydown', (e) => {

    // Maps API e.stopPropagation();
    e.cancelBubble = true;

    // If enter key, or tab key
    if (e.keyCode === 13 || e.keyCode === 9) {
        // If user isn't navigating using arrows and this hasn't ran yet
        if (!hasDownBeenPressed && !e.hasRanOnce) {
            google.maps.event.trigger(e.target, 'keydown', {
                keyCode: 40,
                hasRanOnce: true,
            });
        }
    }
});

 // Clear the input on focus, reset hasDownBeenPressed
searchInput.addEventListener('focus', () => {
    hasDownBeenPressed = false;
    searchInput.value = '';
});

// place_changed GoogleMaps listener when we do submit
google.maps.event.addListener(autocomplete, 'place_changed', function() {

    // Get the place info from the autocomplete Api
    const place = autocomplete.getPlace();

    //If we can find the place lets go to it
    if (typeof place.address_components !== 'undefined') {          
        // reset hasDownBeenPressed in case they don't unfocus
        hasDownBeenPressed = false;
    }

});
Callam
  • 949
  • 1
  • 14
  • 22
  • 1
    this seems to cause an error `TypeError: a.stopPropagation is not a function error` any thoughts on this? – RobbTe Dec 22 '21 at 13:16
4

This is the easiest way that solved to me:

autocomplete.addListener('place_changed', function() {
  if(event.keyCode == 13 || event.keyCode == 9) { // detect the enter key
    var firstValue = $(".pac-container .pac-item:first").text(); // assign to this variable the first string from the autocomplete dropdown
     }
    $('#search-address').val(firstValue); // add this string to input
    console.log(firstValue); // display the string on your browser console to check what it is
   //(...) add the rest of your code here
  });
}
Fillype Farias
  • 622
  • 1
  • 8
  • 20
  • I modified above fiunction and it work for me like a charm. // listner to make kepdown to true and selecet first search result this.searchElementRef.nativeElement.addEventListener('keydown', (e) => { if (e.keyCode === 13 || e.keyCode === 9) { var firstValue = $('.pac-container .pac-item:first').text(); // If user isn't navigating using arrows and this hasn't ran yet this.txtValue = firstValue; google.maps.event.trigger(e.target, 'keydown', { keyCode: 40, hasRanOnce: true, }); } }); – Techiemanu Oct 26 '20 at 03:52
  • this leads to an error for me. what is the event object in this scope? you did not mention in the function arguments – Ali Samie Feb 16 '23 at 09:51
2

This is what I've done and it works:

HTML:

<input name="location" id="autocomplete" autocomplete="off" type="text" class="textbx" placeholder="Enter Destination" required>

googleautocompletecustomized.js:

        function initialize() {
      // Create the autocomplete object, restricting the search
      // to geographical location types.
      if($('#autocomplete').length){
          autocomplete = new google.maps.places.Autocomplete(
              (document.getElementById('autocomplete')),
              {
                types: ['(regions)'],
                componentRestrictions: {country: "in"}
              });
          google.maps.event.addListener(autocomplete, 'place_changed', function() {
            $('#autocomplete').closest('form').data('changed', true);
            fillInAddress();
          });         
      }

    //select first result
        $('#autocomplete').keydown(function (e) {
            if (e.keyCode == 13 || e.keyCode == 9) {
                $(e.target).blur();
                if($(".pac-container .pac-item:first span:eq(3)").text() == "")
                    var firstResult = $(".pac-container .pac-item:first .pac-item-query").text();
                else
                    var firstResult = $(".pac-container .pac-item:first .pac-item-query").text() + ", " + $(".pac-container .pac-item:first span:eq(3)").text();

                var geocoder = new google.maps.Geocoder();
                geocoder.geocode({"address":firstResult }, function(results, status) {
                    if (status == google.maps.GeocoderStatus.OK) {
                        placeName = results[0];
                        e.target.value = firstResult;
                        fillInAddress(placeName);
                        $('#datetimepicker1 .input-group-addon').click();
                    }
                });
            }

        });
    }

// [START region_fillform]
function fillInAddress(place) {
  // Get the place details from the autocomplete object.
  if(!place)
    var place = autocomplete.getPlace();

  for (var component in componentForm) {
    document.getElementById(component).value = '';
    document.getElementById(component).disabled = false;
  }

  // Get each component of the address from the place details
  // and fill the corresponding field on the form.
  for (var i = 0; i < place.address_components.length; i++) {
    var addressType = place.address_components[i].types[0];
    if (componentForm[addressType]) {
      var val = place.address_components[i][componentForm[addressType]];
      document.getElementById(addressType).value = val;

    }
  }

}
Gediminas Masaitis
  • 3,172
  • 14
  • 35
Fronto
  • 374
  • 2
  • 12
0

If you are using the Angular 2,4, 5 & 6, Please find the answer in this below link

Angular 6, AGM selects first address in google autocomplete

Angular AGM autocomplete first address suggestion

saravana va
  • 1,081
  • 1
  • 13
  • 17
-1

Put the input-element outside the form-element. Populate the form with javascript.

document.getElementById("adress").value = place.formatted_address;
Gustav
  • 2,902
  • 1
  • 25
  • 31
-1

I did some work around this and now I can force select 1st option from google placces using angular js and angular Autocomplete module.
Thanks to kuhnza
my code

<form method="get" ng-app="StarterApp"  ng-controller="AppCtrl" action="searchresults.html" id="target" autocomplete="off">
   <br/>
    <div class="row">
    <div class="col-md-4"><input class="form-control" tabindex="1" autofocus g-places-autocomplete force-selection="true"  ng-model="user.fromPlace" placeholder="From Place" autocomplete="off"   required>
    </div>
        <div class="col-md-4"><input class="form-control" tabindex="2"  g-places-autocomplete force-selection="true"  placeholder="To Place" autocomplete="off" ng-model="user.toPlace" required>
    </div>
    <div class="col-md-4"> <input class="btn btn-primary"  type="submit" value="submit"></div></div><br /><br/>
    <input class="form-control"  style="width:40%" type="text" name="sourceAddressLat" placeholder="From Place Lat" id="fromLat">
    <input class="form-control"  style="width:40%"type="text" name="sourceAddressLang" placeholder="From Place Long" id="fromLong">
    <input class="form-control"  style="width:40%"type="text" name="sourceAddress" placeholder="From Place City" id="fromCity">
    <input class="form-control"  style="width:40%"type="text" name="destinationAddressLat" placeholder="To Place Lat" id="toLat">
    <input class="form-control"  style="width:40%"type="text" name="destinationAddressLang" placeholder="To Place Long"id="toLong">
    <input class="form-control"  style="width:40%"type="text" name="destinationAddress"placeholder="To Place City" id="toCity">
</form>

Here is a Plunker
Thank you.

Murali
  • 409
  • 11
  • 17