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!

Author: Daniel Grezo, 2011-10-23

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

    });
});

Http://jsfiddle.net/dodger/pbbhH /

 41
Author: dodger,
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>
 152
Author: amirnissim,
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);
    }
});
 20
Author: Tomasz Matuszczyk,
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:

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

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

 12
Author: Klokan Technologies,
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.

 9
Author: AdamSchuld,
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.

 7
Author: Tony Brasunas,
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 /

 2
Author: Lars-Olof Kreim,
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.

 2
Author: CodyEngel,
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.

 1
Author: Joe,
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;
});
 1
Author: Gangadhar Jannu,
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.

 1
Author: Murali,
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]);
});
 0
Author: alt,
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]);
    };
 0
Author: 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
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;
    }

});
 0
Author: Callam,
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?

 -1
Author: Tobias Hartmann,
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