Google maps Places API V3 автозаполнение-выберите первый вариант при вводе



Я успешно реализовал функцию автозаполнения Google Maps Places V3 на моем поле ввода в соответствии с http://code.google.com/intl/sk-SK/apis/maps/documentation/javascript/places.html#places_autocomplete. это работает хорошо, однако я хотел бы знать, как я могу сделать его выбрать первый вариант из предложений, когда пользователь нажимает enter. Я думаю, мне понадобится магия JS, но я очень новичок в JS и не знаю, с чего начать.



спасибо вперед!

1001   15  

15 ответов:

У меня была такая же проблема при реализации автозаполнения на сайте я недавно работал. Это решение я придумал:

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

вот решение, которое не делает запрос геокодирования, который может вернуть неверный результат:http://jsfiddle.net/amirnissim/2D6HW/

она имитирует down-arrow нажатие всякий раз, когда пользователь нажимает return внутри поля автозаполнения. Элемент событие запускается перед возвращение событие, поэтому он имитирует пользователя, выбирающего первое предложение с помощью клавиатуры.

вот код (проверено на Chrome и 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>

вот пример реального, не хакерского, решения. Он не использует никаких хаков браузера и т. д., Просто методы из общедоступного API, предоставленного Google и документированного здесь: Google Maps API

единственным недостатком является то, что дополнительные запросы к Google требуются, если пользователь не выбирает элемент из списка. Преимущество заключается в том, что результат всегда будет правильным, поскольку запрос выполняется идентично запросу внутри автозаполнения. Второй плюс заключается в том, что только используя общедоступные методы API и не полагаясь на внутреннюю структуру HTML виджета автозаполнения, мы можем быть уверены, что наш продукт не сломается, если Google внесет изменения.

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

кажется, есть гораздо лучшее и чистое решение: использовать google.maps.places.SearchBox вместо google.maps.places.Autocomplete. Код почти такой же, просто первый из нескольких мест. При нажатии Enter возвращается правильный список-так что он выбегает из коробки и нет необходимости в хаки.

Смотрите пример HTML страницы:

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

В соответствующий фрагмент кода:

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

полный исходный код примера находится по адресу:https://gist.github.com/klokan/8408394

для Google Places Autocomplete V3 лучшим решением для этого является два запроса API.

здесь скрипка

причина, по которой ни один из других ответов не был достаточным, заключается в том, что они либо использовали jquery для имитации событий (hacky), либо использовали геокодер или поле поиска Google Places который не всегда соответствует результатам автозаполнения. Вместо этого мы будем использовать сервис автозаполнения Google, как описано здесь только javascript (без jquery)

ниже подробно описано наиболее кросс-браузер совместимое решение с использованием собственных API Google для создания окна автозаполнения, а затем повторно запустить запрос, чтобы выбрать первый вариант.

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

единственная проблема заключается в том, что собственная реализация возвращает другую структуру данных, хотя информация одна и та же. Отрегулируйте соответственно.

вот рабочий ответ на 2018 год.

это сочетает в себе лучшие ответы на этой странице, использует только чистый JS, и написано в простой ES6. Нет необходимости в jQuery, 2-м запросе API или IIFE.

во-первых, если у вас есть что-то вроде этого уже определять свой адрес:

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

затем добавьте это в следующую строку:

enableEnterKey(field)

а затем в другом месте в вашем скрипте, чтобы сохранить эту функциональность приятно и отдельный в коде, добавьте это:

  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
  }

вы должны быть хорошо идти. По существу,enableEnterKey() функция захватывает каждое нажатие клавиши return / enter в input поле и имитирует нажатие клавиши со стрелкой вниз вместо этого. Он также хранит и повторно связывает слушателей и события для поддержания всех функций ваших карт Google Autocomplete().

С очевидной благодарностью к более ранним ответам для большей части этого кода, в частности амирниссима и Александра Шварцмана.

Я просто хочу написать небольшое расширение для ответ amirnissim
опубликованный скрипт не поддерживает IE8, потому что " событие.который " кажется, всегда пуст в IE8.
Чтобы решить эту проблему, вам просто нужно дополнительно проверить " событие.ключ":

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/

ни один из этих ответов, казалось, работали для меня. Они получат общее местоположение, но на самом деле не будут перемещаться в фактическое место, которое я искал. Внутри себя .pac-item вы можете фактически получить только адрес (имя места исключено), выбрав $('.pac-item: first').детей()[2].текстового содержимого

Итак, вот мое решение:

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

Я знаю, что на этот вопрос уже был дан ответ, но решил, что я бы бросил свои 2 цента на всякий случай, если у кого-то еще была такая же проблема, как мне.

@benregn @amirnissim я думаю, что ошибка выбора происходит от:

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

класс pac-selected должно быть pac-item-selected, что объясняет, почему !suggestion_selected всегда оценивайте значение true, что приводит к неправильному выбору местоположения при нажатии клавиши enter после использования "keyup" или "keydown" для выделения нужного местоположения.

Как насчет этого?

$("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-й вариант из google placces с помощью углового js и углового модуля автозаполнения.
Спасибо kuhnza
мой код

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

здесь Plunker
Спасибо.

дом на amimissim это, Я представляю небольшую альтернативу, используя API Google для обработки событий в кросс-браузере (решение amimissim, похоже, не работает в IE8).

мне тоже пришлось менять pac-item.pac-selected to pac-item-refresh.pac-selected как кажется, результаты div класс изменился. Это делает нажатие ENTER по предложению работы (вместо того, чтобы выбрать следующий вниз).

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

просто чистая версия javascript (без jquery) решения великого амирниссима:

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

рабочее решение, которое слушает, если пользователь начал перемещаться по списку с помощью клавиатуры, а не запускать ложную навигацию каждый раз

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

вот важные биты

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

});

Я исследовал это немного, так как у меня та же проблема. Что мне не понравилось в предыдущих решениях, так это то, что автозаполнение уже запустило AutocompleteService, чтобы показать прогнозы. Поэтому прогнозы должны быть где-то и не должны загружаться снова.

я узнал, что предсказания места inkl. place_id хранящийся в

Autocomplete.gm_accessors_.place.Kc.l

и вы сможете получить много данных из записей [0].data. Имхо, это быстрее и лучше, чтобы получить местоположение с помощью place_id вместо адресных данных. Этот очень странный выбор объекта кажется мне не очень хорошим, Тхо.

знаете ли вы, если есть лучший способ получить первое предсказание из автозаполнения?

Comments

    Ничего не найдено.