Completamento automatico di Google Maps Places API V3: seleziona la prima opzione su invio

Ho implementato con successo la funzione di completamento automatico di Google Maps Places V3 nella mia casella di input come per http://code.google.com/intl/sk-SK/apis/maps/documentation/javascript/places.html#places_autocomplete . Funziona bene, tuttavia mi piacerebbe sapere come posso farlo selezionare la prima opzione dai suggerimenti quando un utente preme Invio. Credo che avrei bisogno di un po ‘di magia JS, ma sono molto nuovo a JS e non so da dove cominciare.

Grazie in anticipo!

Ho avuto lo stesso problema durante l’implementazione del completamento automatico su un sito su cui ho lavorato di recente. Questa è la soluzione che ho trovato:

$("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/

Ecco una soluzione che non effettua una richiesta di geocodifica che potrebbe restituire un risultato errato: http://jsfiddle.net/amirnissim/2D6HW/

Simula una pressione tasto a down-arrow ogni volta che l’utente raggiunge il return all’interno del campo di completamento automatico. L’evento viene triggersto prima dell’evento di ritorno in modo da simulare l’utente che seleziona il primo suggerimento utilizzando la tastiera.

Ecco il codice (testato su Chrome e Firefox):

    

Ecco un esempio di una soluzione reale, non hacky. Non utilizza hacks del browser, ecc., Solo metodi dell’API pubblica fornita da Google e documentata qui: API di Google Maps

L’unico svantaggio è che sono richieste ulteriori richieste a Google se l’utente non seleziona una voce dall’elenco. Il vantaggio è che il risultato sarà sempre corretto poiché la query viene eseguita in modo identico alla query all’interno del completamento automatico. Il secondo vantaggio è che utilizzando solo i metodi API pubblici e non facendo affidamento sulla struttura HTML interna del widget Completamento automatico, possiamo essere sicuri che il nostro prodotto non si interromperà se Google apporta modifiche.

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

Sembra che ci sia una soluzione molto migliore e pulita: per utilizzare google.maps.places.SearchBox invece di google.maps.places.Autocomplete . Un codice è quasi lo stesso, basta prendere il primo da più posti. Premendo il tasto Invio, viene restituito l’elenco corretto, quindi viene eseguito immediatamente e non è necessario alcuno.

Vedi la pagina HTML di esempio:

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

Lo snippet di codice pertinente è:

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

Il codice sorgente completo dell’esempio è all’indirizzo: https://gist.github.com/klokan/8408394

Per il completamento automatico di Google Places V3, la soluzione migliore è costituita da due richieste API.

Ecco il violino

Il motivo per cui nessuna delle altre risposte è stata sufficiente perché hanno utilizzato jquery per simulare eventi (hacky) o utilizzato Geocoder o la casella di ricerca di Google Places che non corrisponde sempre ai risultati del completamento automatico . Invece, quello che faremo è utilizzare il servizio di autocomplete di Google come descritto qui con solo javascript (senza jquery)

Di seguito viene descritta la soluzione più compatibile con più browser utilizzando le API native di Google per generare la casella di completamento automatico e quindi rieseguire la query per selezionare la prima opzione.

  

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

 

L’unica lamencanvas è che l’implementazione nativa restituisce una diversa struttura di dati sebbene le informazioni siano le stesse. Regolare di conseguenza.

Ecco una risposta operativa per il 2018.

Questo combina le migliori risposte in questa pagina, usa solo JS puro ed è scritto in ES6 semplice. Nessuna jQuery, seconda richiesta API o IIFE necessaria.

Innanzitutto, supponendo che tu abbia già impostato qualcosa di simile per identificare il campo dell’indirizzo:

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

Quindi aggiungere questo sulla riga successiva:

 enableEnterKey(field) 

E poi altrove nel tuo script, per mantenere questa funzionalità bella e separata nel tuo codice, aggiungi questo:

  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 } 

Dovresti essere bravo ad andare. In sostanza, la funzione enableEnterKey() acquisisce ogni pressione di ritorno / invio nel campo di input e simula invece un tasto di scelta rapida. Memorizza e ricollega listener ed eventi per mantenere tutte le funzionalità del tuo Autocomplete() Google Maps Autocomplete() .

Con ovvi ringraziamenti alle risposte precedenti per la maggior parte di questo codice, particolare amirnissim e Alexander Schwarzman.

Voglio solo scrivere un piccolo miglioramento per la risposta di amirnissim
Lo script pubblicato non supporta IE8, perché “event.which” sembra essere sempre vuoto in IE8.
Per risolvere questo problema è sufficiente controllare ulteriormente “event.keyCode”:

 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/

Nessuna di queste risposte sembrava funzionare per me. Avrebbero ottenuto la posizione generale, ma in realtà non avrebbero effettuato il pan nel posto reale che ho cercato. All’interno di .pac-item puoi effettivamente ottenere solo l’indirizzo (nome del luogo escluso) selezionando $ (‘. Pac-item: first’). Children () [2] .textContent

Quindi ecco la mia soluzione:

 $("#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); } }); } 

So che questa domanda ha già avuto risposta, ma ho pensato di buttare i miei 2 centesimi nel caso in cui qualcun altro avesse lo stesso problema di me.

@benregn @amirnissim Penso che l’errore di selezione provenga da:

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

La class pac-selected deve essere pac-selected in pac-item-selected , il che spiega perché !suggestion_selected valuta sempre true, causando la posizione errata da selezionare quando si preme il tasto invio dopo aver usato “keyup” o “keydown” per evidenziare la posizione desiderata .

Cosa ne pensi di questo?

 $("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; }); 

Ho fatto un po ‘di lavoro su questo e ora posso forzare selezionare 1a opzione da google placces utilizzando il modulo angular js e il modulo di completamento automatico angular.
Grazie a kuhnza
il mio codice

 



Ecco un Plunker
Grazie.

Basandomi sulla risposta di amimissim , presento una leggera alternativa, utilizzando l’API di Google per gestire gli eventi in modo cross browser (la soluzione di amimissim non sembra funzionare in IE8).

Ho anche dovuto modificare pac-item.pac-selected in pac-item-refresh.pac-selected poiché sembra che la class di risultati div sia cambiata. Ciò rende premuto ENTER su un lavoro di suggerimento (piuttosto che selezionare il successivo in basso).

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

Solo una versione javascript pura (senza jquery) della grande soluzione di 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]); }; 

Soluzione di lavoro che ascolta se l’utente ha iniziato a spostarsi verso il basso nell’elenco con la tastiera anziché triggersre la falsa navigazione ogni volta

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

Ecco i bit importanti

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

Ho studiato questo un po ‘dal momento che ho lo stesso problema. Quello che non mi è piaciuto delle soluzioni precedenti era che il completamento automatico ha già triggersto AutocompleteService per mostrare le previsioni. Pertanto, le previsioni dovrebbero essere da qualche parte e non dovrebbero essere caricate di nuovo.

Ho scoperto che le previsioni di place inkl. place_id è memorizzato in

 Autocomplete.gm_accessors_.place.Kc.l 

e sarai in grado di ottenere molti dati dai record [0].data . Imho, è più veloce e migliore ottenere la posizione utilizzando place_id anziché i dati dell’indirizzo. Questa selezione di oggetti molto strana non mi sembra molto buona, comunque.

Sai, se esiste un modo migliore per recuperare la prima previsione dal completamento automatico?