Google maps Places API V3 autocompletar-seleccione la primera opción en entrar
He implementado con éxito la función de autocompletar de Google Maps Places V3 en mi caja de entrada según http://code.google.com/intl/sk-SK/apis/maps/documentation/javascript/places.html#places_autocomplete. Funciona muy bien, sin embargo, me encantaría saber cómo puedo hacer que seleccione la primera opción de las sugerencias cuando un usuario presiona enter. Supongo que necesitaría algo de magia JS, pero soy muy nuevo en JS y no sé por dónde empezar.
Gracias de antemano!
15 answers
Tuve el mismo problema al implementar autocompletar en un sitio en el que trabajé recientemente. Esta es la solución que se me ocurrió:
$("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");
}
});
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-08 03:54:04
Aquí hay una solución que no hace una solicitud de geocodificación que pueda devolver un resultado incorrecto: http://jsfiddle.net/amirnissim/2D6HW/
Simula una tecla down-arrow
cada vez que el usuario pulsa return
dentro del campo de autocompletar. Las ↓ el evento se activa antes del evento return por lo que simula que el usuario selecciona la primera sugerencia usando el teclado.
Aquí está el código (probado en Chrome y 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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-30 08:50:32
Aquí hay un ejemplo de una solución real, no hackeada. No usa ningún hacks de navegador, etc., solo métodos de la API pública proporcionada por Google y documentada aquí: API de Google Maps
El único inconveniente es que se requieren solicitudes adicionales a Google si el usuario no selecciona un elemento de la lista. La ventaja es que el resultado siempre será correcto ya que la consulta se realiza de forma idéntica a la consulta dentro del autocompletado. La segunda ventaja es que solo usando público Métodos API y no depender de la estructura HTML interna del widget de autocompletar, podemos estar seguros de que nuestro producto no se romperá si Google realiza cambios.
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);
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-06 16:48:47
Parece que hay una solución mucho mejor y limpia: Usar google.maps.places.SearchBox
en lugar de google.maps.places.Autocomplete
.
Un código es casi lo mismo, solo obtener el primero de varios lugares. Al presionar la tecla Enter, se devuelve la lista correcta, por lo que se agota y no hay necesidad de hacks.
Ver la página HTML de ejemplo:
El fragmento de código relevante 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);
}
});
El código fuente completo del ejemplo está en: https://gist.github.com/klokan/8408394
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-13 21:25:51
Para Google Places Autocomplete V3, la mejor solución para esto son dos solicitudes de API.
Aquí está el violín
La razón por la que ninguna de las otras respuestas fue suficiente es porque usaron jquery para imitar eventos (hacky) o usaron Geocoder o Google Places Search box que no siempre coincide con los resultados de autocompletar. En su lugar, lo que haremos es usar el Servicio de Autocompletar de Google como se detalla aquí con solo javascript (no jquery)
A continuación se detalla la solución más compatible con navegadores que utiliza las API nativas de Google para generar el cuadro de autocompletar y luego volver a ejecutar la consulta para seleccionar la primera opción.
<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 única queja es que la implementación nativa devuelve una estructura de datos diferente aunque la información es la misma. Ajuste en consecuencia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-16 00:31:20
Aquí hay una respuesta de trabajo para 2018.
Esto combina las mejores respuestas en esta página, utiliza solo JS puro, y está escrito en ES6 directo. No se necesita jQuery, 2nd API request o IIFE.
Primero, asumiendo que ya tienes algo como esto configurado para identificar tu campo de dirección:
const field = document.getElementById('address-field')
const autoComplete = new google.maps.places.Autocomplete(field)
autoComplete.setTypes(['address'])
Luego agrega esto en la siguiente línea:
enableEnterKey(field)
Y luego en otra parte de su script, para mantener esta funcionalidad agradable y separada en su código, agregue esto:
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
}
Usted debe ser bueno para ir. Esencialmente, la función enableEnterKey()
captura cada tecla de retorno/entrada en el campo input
y simula una tecla de flecha hacia abajo en su lugar. También almacena y rebinds oyentes y eventos para mantener toda la funcionalidad de su Google Maps Autocomplete()
.
Con obvias gracias a las respuestas anteriores para la mayor parte de este código, en particular amirnissim y Alexander Schwarzman.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-26 17:34:53
Solo quiero escribir una pequeña mejora para la respuesta de amirnissim
El script publicado no es compatible con IE8, porque " evento.que " parece estar siempre vacío en IE8.
Para resolver este problema solo tiene que comprobar adicionalmente para "evento.Código clave":
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 /
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:14
Ninguna de estas respuestas parecía funcionar para mí. Conseguirían la ubicación general pero no llegarían al lugar que busqué. Dentro del .pac-item puede obtener solo la dirección (nombre del lugar excluido) seleccionando $('.pac-item: first').niños()[2].textContent
Así que aquí está mi solución:
$("#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);
}
});
}
Sé que esta pregunta ya fue respondida, pero pensé que arrojaría mis 2 centavos por si alguien más tenía el mismo problema que yo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-12 01:21:50
@benregn @amirnissim Creo que el error de selección viene de:
var suggestion_selected = $(".pac-item.pac-selected").length > 0;
La clase pac-selected
debe ser pac-item-selected
, lo que explica por qué !suggestion_selected
siempre evalúa a true, haciendo que la ubicación incorrecta se seleccione cuando se presiona la tecla enter después de usar 'keyup' o 'keydown' para resaltar la ubicación deseada.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-30 18:59:08
¿Qué tal esto?
$("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;
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-15 06:41:02
Hice algo de trabajo alrededor de esto y ahora puedo forzar la opción select 1st de Google placces usando angular js y angular Autocomplete módulo.
Gracias a kuhnza
mi código
<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>
Aquí hay un Émbolo
Agradecer.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-26 05:24:20
Sobre la base de respuesta de amimissim , Presento una ligera alternativa, utilizando la API de Google para manejar los eventos de una manera de navegador cruzado (solución de amimissim no parece funcionar en IE8).
También tuve que cambiar pac-item.pac-selected
a pac-item-refresh.pac-selected
ya que parece que la clase div de resultados ha cambiado. Esto hace que presionar ENTER
en una sugerencia funcione (en lugar de seleccionar la siguiente hacia abajo).
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]);
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:02
Solo una versión javascript pura (sin jquery) de la gran solución de 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]);
};
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-12 13:26:49
Solución de trabajo que escucha si el usuario ha comenzado a navegar por la lista con el teclado en lugar de activar la navegación falsa cada vez
Https://codepen.io/callam/pen/RgzxZB
Aquí están los bits 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;
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-24 11:01:49
Investigué esto un poco ya que tengo el mismo problema. Lo que no me gustó de las soluciones anteriores fue, que el autocompletar ya disparó el AutocompleteService para mostrar las predicciones. Por lo tanto, las predicciones deben estar en algún lugar y no deben cargarse de nuevo.
Me enteré de que las predicciones de lugar inkl. place_id se almacena en
Autocomplete.gm_accessors_.place.Kc.l
Y usted será capaz de obtener una gran cantidad de datos de los registros [0].data
. En mi humilde opinión, es más rápido y mejor ubicación usando el place_id en lugar de datos de dirección. Esta extraña selección de objetos no me parece muy buena, tho.
¿Sabes, si hay una mejor manera de recuperar la primera predicción de la autocompletar?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-18 13:16:15