javascript - example - google.maps.places.autocomplete options
Google Maps Places API V3 autocompletar: seleccione la primera opciĆ³n al ingresar (14)
Implementé con éxito la función de autocompletar de Google Maps Places V3 en mi cuadro 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 presione enter. Supongo que necesitaría algo de magia JS, pero soy muy nuevo en JS y no sé por dónde empezar.
¡Gracias por adelantado!
¿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;
});
@benregn @amirnissim Creo que el error de selección proviene 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 como verdadero, haciendo que se seleccione la ubicación incorrecta cuando se pulsa la tecla enter después de usar ''keyup'' o ''keydown'' para resaltar la ubicación deseada .
Aquí hay un ejemplo de una solución real, no hacky. No utiliza ningún hack de navegador, etc., solo los métodos de la API pública proporcionada por Google y documentada aquí: Google Maps API
El único inconveniente es que se requieren solicitudes adicionales a Google si el usuario no selecciona un elemento de la lista. Lo bueno es que el resultado siempre será correcto ya que la consulta se realiza de forma idéntica a la consulta dentro de Autocompletar. El segundo aspecto positivo es que al utilizar solo métodos API públicos y no confiar en la estructura HTML interna del widget 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);
}
});
Aquí hay una solución que no hace una solicitud de geocodificación que puede devolver un resultado incorrecto: http://jsfiddle.net/amirnissim/2D6HW/
Simula una pulsación de tecla de down-arrow
cada vez que el usuario golpea return
dentro del campo de autocompletar. El evento ↓ se activa antes del evento de retorno , 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>
Hice un poco de trabajo alrededor de esto y ahora puedo forzar la selección de la primera opción desde google placces usando angular js y el módulo angular Autocompletar.
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 Plunker
Gracias.
Investigué esto un poco ya que tengo el mismo problema. Lo que no me gustó de las soluciones anteriores fue que el autocompletado ya activó AutocompleteService para mostrar las predicciones. Por lo tanto, las predicciones deben estar en algún lugar y no deben cargarse nuevamente.
Descubrí que las predicciones de lugar inkl. place_id se almacena en
Autocomplete.gm_accessors_.place.Kc.l
y podrá obtener una gran cantidad de datos de los registros [0].data
. Imho, es más rápido y mejor obtener la ubicación usando el place_id en lugar de los datos de dirección. Esta muy extraña selección de objetos no me parece muy buena, aunque.
¿Sabes si hay una mejor manera de recuperar la primera predicción de la autocompletar?
Ninguna de estas respuestas pareció funcionar para mí. Obtendrían la ubicación general pero no se desplazarían al lugar real que busqué. Dentro del elemento .pac, en realidad puede obtener solo la dirección (nombre del lugar excluido) seleccionando $ (''.pac-item: first''). Children () [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 invertiría mis 2 centavos por si acaso alguien tuviera el mismo problema que yo.
Parece que hay una solución mucho mejor y más limpia: utilizar google.maps.places.SearchBox
lugar de google.maps.places.Autocomplete
. Un código es casi lo mismo, simplemente obtener el primero de múltiples lugares. Al presionar Enter, se devuelve la lista correcta, por lo que se ejecuta fuera de la caja y no hay necesidad de hacks.
Vea la página HTML de ejemplo:
El fragmento de código relevante es:
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
Sobre la base de la respuesta de amimissim , presento una ligera alternativa, utilizando la API de Google para manejar los eventos de forma cruzada (la 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).
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 quiero escribir una pequeña mejora para la respuesta de amirnissim
El script publicado no es compatible con IE8, porque "event.which" parece estar siempre vacío en IE8.
Para resolver este problema, solo necesita verificar adicionalmente "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/
Solo una versión pura de javascript (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]);
};
Tuve el mismo problema al implementar la función de 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");
}
});
});
Aquí hay una respuesta de trabajo para 2018.
Esto combina las mejores respuestas en esta página, utilizando solo JS puro y escrito en ES6 comprensible. (No se necesita jQuery, solicitud de la segunda API o IIFE).
En primer lugar, suponiendo que tienes algo como esto ya 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''])
A continuación, agregue esto en la próxima 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 presionada return / enter en el campo de input
y simula una tecla presionada hacia abajo. También almacena y vuelve a enlazar oyentes y eventos para mantener la funcionalidad restante de Autocomplete()
Google Maps Autocomplete()
.
Con obvio agradecimiento a las respuestas anteriores para la mayoría de este código, en particular amirnissim y Alexander Schwarzman.
Para Google Places Autocomplete V3, la mejor solución para esto son dos solicitudes de API.
Aquí está el fiddle
La razón por la que ninguna de las otras respuestas fue suficiente es porque usaron jquery para imitar eventos (hacky) o utilizaron el cuadro de Geocoder o Búsqueda de Google Places, que no siempre coincide con los resultados de autocompletar . En cambio, lo que haremos es utilizar el Servicio de autocompletado de Google como se detalla aquí con solo javascript (no jquery)
A continuación se detalla la solución más compatible con varios navegadores que usa API de Google nativas 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. Ajustar en consecuencia.