Google maps place API V3 autocomplete-sélectionnez la première option enter

J'ai mis en œuvre avec succès la fonctionnalité v3 autocomplete de Google Maps sur ma boîte de saisie selon http://code.google.com/intl/sk-SK/apis/maps/documentation/javascript/places.html#places_autocomplete . Il fonctionne bien, mais je voudrais savoir comment je peux lui faire choisir la première option parmi les suggestions quand un utilisateur appuie sur Entrée. Je suppose que j'aurais besoin d'un peu de magie JS, mais je suis très nouveau à JS et je ne sais pas par où commencer.

Merci à l'avance!

53
demandé sur Daniel Grezo 2011-10-23 13:54:37

15 réponses

j'ai eu le même problème lors de la mise en œuvre d'autocomplete sur un site sur lequel j'ai travaillé récemment. C'est la solution que j'ai trouvée:

$("input").focusin(function () {
    $(document).keypress(function (e) {
        if (e.which == 13) {
            var firstResult = $(".pac-container .pac-item:first").text();

            var geocoder = new google.maps.Geocoder();
            geocoder.geocode({"address":firstResult }, function(results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    var lat = results[0].geometry.location.lat(),
                        lng = results[0].geometry.location.lng(),
                        placeName = results[0].address_components[0].long_name,
                        latlng = new google.maps.LatLng(lat, lng);

                        $(".pac-container .pac-item:first").addClass("pac-selected");
                        $(".pac-container").css("display","none");
                        $("#searchTextField").val(firstResult);
                        $(".pac-container").css("visibility","hidden");

                    moveMarker(placeName, latlng);

                }
            });
        } else {
            $(".pac-container").css("visibility","visible");
        }

    });
});

http://jsfiddle.net/dodger/pbbhH /

41
répondu dodger 2011-12-08 03:54:04

Voici une solution qui ne fait pas une requête de géocodage qui peut retourner un résultat incorrect: http://jsfiddle.net/amirnissim/2D6HW/

il simule une down-arrow keypress chaque fois que l'utilisateur tape return à l'intérieur du champ autocomplete. L'événement est déclenché avant l'événement return de sorte qu'il simule l'utilisateur en sélectionnant la première suggestion à l'aide du clavier.

voici le code (testé sur Chrome et Firefox) :

<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false&libraries=places"></script>
<script>
    var pac_input = document.getElementById('searchTextField');

    (function pacSelectFirst(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]);
        }

        input.addEventListener = addEventListenerWrapper;
        input.attachEvent = addEventListenerWrapper;

        var autocomplete = new google.maps.places.Autocomplete(input);

    })(pac_input);
</script>
152
répondu amirnissim 2014-03-30 08:50:32

voici un exemple de solution réelle, non-hacky. Il n'utilise aucun piratage de navigateur, etc, juste des méthodes à partir de L'API publique fourni par Google et documenté ici: Google Maps API

le seul inconvénient est que des requêtes supplémentaires à Google sont nécessaires si l'utilisateur ne sélectionne pas un élément de la liste. Le bon côté est que le résultat sera toujours correct car la requête est exécutée de manière identique à la requête à l'intérieur de L'AutoComplete. Le second avantage est qu'en utilisant uniquement les méthodes API publiques et en ne s'appuyant pas sur la structure HTML interne du widget AutoComplete, nous pouvons être sûrs que notre produit ne se casse pas si Google fait des changements.

var input = /** @type {HTMLInputElement} */(document.getElementById('searchTextField'));
var autocomplete = new google.maps.places.Autocomplete(input);  
// These are my options for the AutoComplete
autocomplete.setTypes(['(cities)']);
autocomplete.setComponentRestrictions({'country': 'es'});

google.maps.event.addListener(autocomplete, 'place_changed', function() {
    result = autocomplete.getPlace();
    if(typeof result.address_components == 'undefined') {
        // The user pressed enter in the input 
        // without selecting a result from the list
        // Let's get the list from the Google API so that
        // we can retrieve the details about the first result
        // and use it (just as if the user had actually selected it)
        autocompleteService = new google.maps.places.AutocompleteService();
        autocompleteService.getPlacePredictions(
            {
                'input': result.name,
                'offset': result.name.length,
                // I repeat the options for my AutoComplete here to get
                // the same results from this query as I got in the 
                // AutoComplete widget
                'componentRestrictions': {'country': 'es'},
                'types': ['(cities)']
            },
            function listentoresult(list, status) {
                if(list == null || list.length == 0) {
                    // There are no suggestions available.
                    // The user saw an empty list and hit enter.
                    console.log("No results");
                } else {
                    // Here's the first result that the user saw
                    // in the list. We can use it and it'll be just
                    // as if the user actually selected it
                    // themselves. But first we need to get its details
                    // to receive the result on the same format as we
                    // do in the AutoComplete.
                    placesService = new google.maps.places.PlacesService(document.getElementById('placesAttribution'));
                    placesService.getDetails(
                        {'reference': list[0].reference},
                        function detailsresult(detailsResult, placesServiceStatus) {
                            // Here's the first result in the AutoComplete with the exact
                            // same data format as you get from the AutoComplete.
                            console.log("We selected the first item from the list automatically because the user didn't select anything");
                            console.log(detailsResult);
                        }
                    );
                }
            }
        );
    } else {
        // The user selected a result from the list, we can 
        // proceed and use it right away
        console.log("User selected an item from the list");
        console.log(result);
    }
});
20
répondu Tomasz Matuszczyk 2013-07-06 16:48:47

il semble qu'il y ait une solution bien meilleure et propre: utiliser google.maps.places.SearchBox au lieu de google.maps.places.Autocomplete . Un code est presque le même, on obtient juste le premier à partir de plusieurs endroits. En appuyant sur L'entrée la liste correcte est retournée - de sorte qu'il sort de la boîte et il n'y a pas besoin de piratages.

voir l'exemple page HTML:

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

l'extrait de code correspondant est:

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

le code source complet de l'exemple est à: https://gist.github.com/klokan/8408394

12
répondu Klokan Technologies 2014-01-13 21:25:51

pour Google Places Autocomplete V3, la meilleure solution pour cela est deux requêtes API.

voici le violon

la raison pour laquelle aucune des autres réponses n'a suffi est qu'ils ont utilisé jquery pour imiter des événements (hacky) ou utilisé soit Geocoder ou Google Places Search box qui ne correspond pas toujours résultats autocomplete . Au lieu de cela, ce que nous ferons, c'est utiliser Google Autocomplete Service comme détaillé ici avec seulement javascript (no jquery)

ci-dessous est détaillé le plus cross navigateur solution compatible en utilisant natif APIs Google pour générer la boîte autocomplete, puis relancer la requête pour sélectionner la première option.

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&language=en"></script>

Javascript

// For convenience, although if you are supporting IE8 and below
// bind() is not supported
var $ = document.querySelector.bind(document);

function autoCallback(predictions, status) {
    // *Callback from async google places call
    if (status != google.maps.places.PlacesServiceStatus.OK) {
        // show that this address is an error
        pacInput.className = 'error';
        return;
    }

    // Show a successful return
    pacInput.className = 'success';
    pacInput.value = predictions[0].description;
}


function queryAutocomplete(input) {
    // *Uses Google's autocomplete service to select an address
    var service = new google.maps.places.AutocompleteService();
    service.getPlacePredictions({
        input: input,
        componentRestrictions: {
            country: 'us'
        }
    }, autoCallback);
}

function handleTabbingOnInput(evt) {
    // *Handles Tab event on delivery-location input
    if (evt.target.id == "pac-input") {
        // Remove active class
        evt.target.className = '';

        // Check if a tab was pressed
        if (evt.which == 9 || evt.keyCode == 9) {
            queryAutocomplete(evt.target.value);
        }
    }
}

// ***** Initializations ***** //
// initialize pac search field //
var pacInput = $('#pac-input');
pacInput.focus();

// Initialize Autocomplete
var options = {
    componentRestrictions: {
        country: 'us'
    }
};
var autocomplete = new google.maps.places.Autocomplete(pacInput, options);
// ***** End Initializations ***** //

// ***** Event Listeners ***** //
google.maps.event.addListener(autocomplete, 'place_changed', function () {
    var result = autocomplete.getPlace();
    if (typeof result.address_components == 'undefined') {
        queryAutocomplete(result.name);
    } else {
        // returns native functionality and place object
        console.log(result.address_components);
    }
});

// Tabbing Event Listener
if (document.addEventListener) {
    document.addEventListener('keydown', handleTabbingOnInput, false);
} else if (document.attachEvent) { // IE8 and below
    document.attachEvent("onsubmit", handleTabbingOnInput);
}

// search form listener
var standardForm = $('#search-shop-form');
if (standardForm.addEventListener) {
    standardForm.addEventListener("submit", preventStandardForm, false);
} else if (standardForm.attachEvent) { // IE8 and below
    standardForm.attachEvent("onsubmit", preventStandardForm);
}
// ***** End Event Listeners ***** //

HTML

<form id="search-shop-form" class="search-form" name="searchShopForm" action="/impl_custom/index/search/" method="post">
    <label for="pac-input">Delivery Location</label>
        <input id="pac-input" type="text" placeholder="Los Angeles, Manhattan, Houston" autocomplete="off" />
        <button class="search-btn btn-success" type="submit">Search</button>
</form>

la seule plainte est que l'implémentation native renvoie des données différentes structure bien que l'information soit la même. Ajuster en conséquence.

9
répondu AdamSchuld 2015-02-16 00:31:20

Voici une réponse pour 2018.

cela combine les meilleures réponses sur cette page, utilise uniquement js pur, et est écrit en ES6 simple. Pas de jQuery, 2ème demande d'API, ou IIFE nécessaire.

tout d'abord, en supposant que vous avez déjà quelque chose de ce genre mis en place pour identifier votre champ d'adresse:

const field = document.getElementById('address-field') 
const autoComplete = new google.maps.places.Autocomplete(field)
autoComplete.setTypes(['address'])

ajouter ensuite ceci à la ligne suivante:

enableEnterKey(field)

et puis ailleurs dans votre script, pour garder cette fonctionnalité agréable et distinct dans votre code, ajoutez ceci:

  function enableEnterKey(input) {

    /* Store original event listener */
    const _addEventListener = (input.addEventListener) ? input.addEventListener : input.attachEvent

    const addEventListenerWrapper = (type, listener) => {
      if (type === "keydown") {
        /* Store existing listener function */
        const _listener = listener
        listener = (event) => {
          /* Simulate a 'down arrow' keypress if no address has been selected */
          const suggestion_selected = document.getElementsByClassName('pac-item-selected').length > 0
          if (event.which === 13 && !suggestion_selected) {
            const e = JSON.parse(JSON.stringify(event))
            e.which = 40
            e.keyCode = 40
            _listener.apply(input, [e])
          }
          _listener.apply(input, [event])
        }
      }
      _addEventListener.apply(input, [type, listener])
    }

    input.addEventListener = addEventListenerWrapper
    input.attachEvent      = addEventListenerWrapper
  }

Vous devriez être bon d'aller. Essentiellement, la fonction enableEnterKey() capture chaque touche return/enter dans le champ input et simule une touche de flèche vers le bas à la place. Il stocke et rebinds également les auditeurs et les événements pour maintenir toutes les fonctionnalités de votre Google Maps Autocomplete() .

avec des remerciements évidents à des réponses plus tôt pour la plupart de ce code, notamment amirnissim et Alexander Schwarzman.

7
répondu Tony Brasunas 2018-08-26 17:34:53

je veux juste écrire une petite amélioration pour la réponse d'amirnissim

le script posté ne supporte pas IE8, parce que" événement.qui " semble toujours vide en IE8.

Pour résoudre ce problème, il vous suffit de cocher "événement.mot de code":

listener = function (event) {
  if (event.which == 13 || event.keyCode == 13) {
    var suggestion_selected = $(".pac-item.pac-selected").length > 0;
    if(!suggestion_selected){
      var simulated_downarrow = $.Event("keydown", {keyCode:40, which:40})
      orig_listener.apply(input, [simulated_downarrow]);
    }
  }
  orig_listener.apply(input, [event]);
};

js-Fiddle: http://jsfiddle.net/QW59W/107 /

2
répondu Lars-Olof Kreim 2017-05-23 12:18:14

Aucune de ces réponses semblait fonctionner pour moi. Ils auraient l'emplacement général, mais ils n'auraient pas trouvé l'endroit que j'ai cherché. Au sein de la .pac-item vous pouvez en fait obtenir juste l'adresse (nom du lieu exclu) en sélectionnant $('.pac-élément:first').enfants (les)[2].textContent

alors voici ma solution:

$("#search_field").on("keyup", function(e) {
    if(e.keyCode == 13) {
        searchPlaces();
    }
});

function searchPlaces() {
    var $firstResult = $('.pac-item:first').children();
    var placeName = $firstResult[1].textContent;
    var placeAddress = $firstResult[2].textContent;

    $("#search_field").val(placeName + ", " + placeAddress);

    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({"address":placeAddress }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            var lat = results[0].geometry.location.lat(),
                lng = results[0].geometry.location.lng(),
                placeName = results[0].address_components[0].long_name,
                latlng = new google.maps.LatLng(lat, lng);

            map.panTo(latlng);
        }
    });
}

je sais qu'on a déjà répondu à cette question mais j'ai pensé que je donnerais mes 2 cents juste au cas où quelqu'un d'autre serait ayant le même problème que moi.

2
répondu CodyEngel 2014-08-12 01:21:50

@benrgn @amirnissim je pense que l'erreur de sélection vient de:

var suggestion_selected = $(".pac-item.pac-selected").length > 0;

la classe pac-selected doit être pac-item-selected , ce qui explique pourquoi !suggestion_selected évalue toujours à true, ce qui fait que l'emplacement incorrect doit être sélectionné lorsque la touche enter est pressée après avoir utilisé 'keyup' ou 'keydown' pour mettre en évidence l'emplacement désiré.

1
répondu Joe 2013-10-30 18:59:08

et ça?

$("input").keypress(function(event){
            if(event.keyCode == 13 || event.keyCode == 9) {
                $(event.target).blur();
                if($(".pac-container .pac-item:first span:eq(3)").text() == "")
                    firstValue = $(".pac-container .pac-item:first .pac-item-query").text();
                else
                    firstValue = $(".pac-container .pac-item:first .pac-item-query").text() + ", " + $(".pac-container .pac-item:first span:eq(3)").text();
                event.target.value = firstValue;

            } else
                return true;
});
1
répondu Gangadhar Jannu 2014-07-15 06:41:02

j'ai fait un peu de travail autour de cela et maintenant je peux forcer la 1ère option de Google placces en utilisant angular js et angular Autocomplete module.

Merci à kuhnza

mon 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>

voici un Plunker " 151950920

Remercier.

1
répondu Murali 2015-07-26 05:24:20

en me basant sur la réponse d'amimissim , je présente une petite alternative, en utilisant L'API de Google pour gérer les événements dans un navigateur croisé (la solution d'amimissim ne semble pas fonctionner dans IE8).

j'ai aussi dû changer pac-item.pac-selected en pac-item-refresh.pac-selected car il semble que la classe de div de résultats a changé. Cela fait appuyer ENTER sur un travail de suggestion (plutôt que de sélectionner le suivant vers le bas).

var input = document.getElementById('MyFormField');
var autocomplete = new google.maps.places.Autocomplete(input);
google.maps.event.addListener(autocomplete, 'keydown', function(event) {
    var suggestion_selected = $(".pac-item-refesh.pac-selected").length > 0;
    if (event.which == 13 && !suggestion_selected) {
        var simulated_downarrow = $.Event("keydown", {
                    keyCode: 40,
                    which: 40
        });
        this.apply(autocomplete, [simulated_downarrow]);
    }
    this.apply(autocomplete, [event]);
});
0
répondu alt 2017-05-23 12:03:02

juste une version javascript pure (sans jquery) de la solution du grand amirnissim:

listener = function(event) {
      var suggestion_selected = document.getElementsByClassName('.pac-item-selected').length > 0;
      if (event.which === 13 && !suggestion_selected) {
        var e = JSON.parse(JSON.stringify(event));
        e.which = 40;
        e.keyCode = 40;
        orig_listener.apply(input, [e]);
      }
      orig_listener.apply(input, [event]);
    };
0
répondu Alexander Schwarzman 2016-12-12 13:26:49

solution de travail qui écoute si l'Utilisateur a commencé à naviguer dans la liste avec le clavier plutôt que de déclencher la fausse navigation à chaque fois

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

Ici sont les choses importantes

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

});
0
répondu Callam 2018-08-24 11:01:49

j'ai étudié un peu car j'ai le même Problème. Ce que je n'ai pas aimé dans les solutions précédentes, c'est que l'autocomplète a déjà tiré le service D'Autocomplète pour montrer les prédictions. Par conséquent, les prédictions devraient être quelque part et ne devrait pas être chargé à nouveau.

j'ai découvert que les prédictions de place inkl. place_id est stocké dans

Autocomplete.gm_accessors_.place.Kc.l

et vous serez en mesure d'obtenir beaucoup de données à partir de les enregistrements [0].data . Imho, il est plus rapide et mieux d'obtenir l'emplacement en utilisant le place_id au lieu de données d'adresse. Cette sélection d'objets très étrange ne me semble pas très bonne, tho.

savez-vous s'il y a une meilleure façon de récupérer la première prédiction à partir de l'autocomplete?

-1
répondu Tobias Hartmann 2017-12-18 13:16:15