Google maps Places API V3 автозаполнение - выберите первый вариант при вводе - PullRequest
66 голосов
/ 23 октября 2011

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

Заранее спасибо!

Ответы [ 17 ]

161 голосов
/ 28 июля 2012

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

Имитирует нажатие клавиши down-arrow всякий раз, когда пользователь нажимает return в поле автозаполнения.Событие запускается до события 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>
43 голосов
/ 16 ноября 2011

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

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

21 голосов
/ 06 июля 2013

Вот пример реального не хакерского решения.Он не использует взломы браузера и т. Д., Только методы из общедоступного 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);
    }
});
16 голосов
/ 03 апреля 2018

Вот рабочий ответ на 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

    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.key === 'Enter' && !suggestion_selected) {
            const e = JSON.parse(JSON.stringify(event))
            e.key = 'ArrowDown'
            e.code = 'ArrowDown'
            _listener.apply(input, [e])
          }
          _listener.apply(input, [event])
        }
      }
      _addEventListener.apply(input, [type, listener])
    }

    input.addEventListener = addEventListenerWrapper
  }

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

Благодаря очевидным, благодаря более ранним ответам на большую часть этого кода, в частности на amirnissim и Alexander Schwarzman.

12 голосов
/ 14 января 2014

Кажется, есть намного лучшее и чистое решение: использовать 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

9 голосов
/ 23 января 2015

Для 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>

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

2 голосов
/ 15 июля 2014

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

$("input").keypress(function(event) {
  var firstValue = null;
  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;
});
2 голосов
/ 06 декабря 2013

Я просто хочу написать небольшое улучшение для ответа амирниссима Размещенный скрипт не поддерживает IE8, потому что «event.which» в IE8 кажется пустым.Для решения этой проблемы вам просто нужно дополнительно проверить «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/

2 голосов
/ 12 августа 2014

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

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

$("#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 цента на случай, если у кого-то еще возникнет такая же проблема, как и у меня.

2 голосов
/ 15 ноября 2018

Что касается всех ваших ответов, я создал решение, которое идеально подходит для меня.

/**
 * Function that add the google places functionality to the search inputs
 * @private
 */
function _addGooglePlacesInputsAndListeners() {
    var self = this;
    var input = document.getElementById('searchBox');
    var options = {
        componentRestrictions: {country: "es"}
    };

    self.addInputEventListenersToAvoidAutocompleteProblem(input);
    var searchBox = new google.maps.places.Autocomplete(input, options);
    self.addPlacesChangedListener(searchBox, self.SimulatorMapStorage.map);
}

/**
 * A problem exists with google.maps.places.Autocomplete when the user write an address and doesn't selectany options that autocomplete gives him so we have to add some events to the two inputs that we have to simulate the behavior that it should have. First, we get the keydown 13 (Enter) and if it's not a suggested option, we simulate a keydown 40 (keydownArrow) to select the first option that Autocomplete gives. Then, we dispatch the event to complete the request.
 * @param input
 * @private
 */
function _addInputEventListenersToAvoidAutocompleteProblem(input) {
    input.addEventListener('keydown', function(event) {
        if (event.keyCode === 13 && event.which === 13) {
            var suggestion_selected = $(".pac-item-selected").length > 0;
            if (!suggestion_selected) {
                var keyDownArrowEvent = new Event('keydown');
                keyDownArrowEvent.keyCode = 40;
                keyDownArrowEvent.which = keyDownArrowEvent.keyCode;

                input.dispatchEvent(keyDownArrowEvent);
            }
        }
    });
}
<input id="searchBox" class="search-input initial-input" type="text" autofocus>

Надеюсь, что это кому-нибудь поможет.Пожалуйста, не стесняйтесь обсуждать лучший способ сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...