Google 地图 Places API V3 自动完成 - 在输入时选择第一个选项

IT技术 javascript google-maps autocomplete google-maps-api-3
2021-01-23 05:06:22

我已经按照http://web.archive.org/web/20120225114154/http://code.google.com:80/intl/sk-SK/apis/在我的输入框中成功实现了 Google Maps Places V3 自动完成功能地图/文档/javascript/places.html它工作得很好,但是我很想知道如何让它在用户按下 Enter 键时从建议中选择第一个选项。我想我需要一些 JS 魔法,但我对 JS 很陌生,不知道从哪里开始。

提前致谢!

6个回答

这是一个不发出可能返回错误结果的地理编码请求的解决方案: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>
你救了我的一天,非常感谢!
2021-03-13 05:06:22
这个解决方案比接受的解决方案要好得多,我建议重新接受它
2021-03-14 05:06:22
很好的解决方案,谢谢。这是这个问题的正确答案。如果可以的话,我会投票两次。
2021-03-29 05:06:22
@benregn 更改为 var suggestion_selected = $(".pac-item-selected").length > 0;
2021-04-04 05:06:22
这个解决方案有一个小问题。如果用户实际上使用向下箭头键选择了列表中的第一个结果,则选择了下面的结果 - 在这种情况下这是错误的...
2021-04-06 05:06:22

在我最近工作的网站上实现自动完成时,我遇到了同样的问题。这是我想出的解决方案:

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

谢谢!谢谢!!谢谢!!!我不知道您可以使用 Geocoder.geocode() 来获取 address_components。你为我节省了几个小时的工作!再次感谢你!
2021-03-14 05:06:22
就我而言,我做了以下事情: var firstResult = $(".pac-container .pac-item:first").text();var stringMatched = $(".pac-container .pac-item:first").find(".pac-item-query").text(); firstResult = firstResult.replace(stringMatched, stringMatched + " ");这就解决了问题
2021-03-16 05:06:22
你不能只对第一个项目执行 .click() 来触发与你相同的处理。
2021-03-23 05:06:22
你能告诉我如何在“.pac-item-query”之后添加空格,因为现在当我点击 ENTER 时,空格被删除
2021-04-05 05:06:22
此方法的问题在于它返回的地址与第一个自动完成结果不同。它对第一个自动完成结果的显示地址执行地理编码,该地址可能是一个完全不同的地址。例如,输入“jfk”的第一个自动完成结果是正确的地址(JFK Access Road, New York, NY, United States),但地址“JFK Airport, New York, NY”的地理编码结果(显示第一个自动完成的地址)将产生似乎是酒店的地址(144-02 135th Ave, Queens, NY 11436, USA)。
2021-04-09 05:06:22

2020 年的有效答案。

我结合了这个页面上的最佳答案,并用简单的 ES6 编写。不需要 jQuery、第二个 API 请求或 IIFE。

基本上,down-arrow每当用户在自动完成字段内点击返回时,我们都会模拟 ↓ ( ) 按键。

首先,假设在你的 HTML 中你有类似的东西,像这样<input id="address-field">设置你的地址字段的标识:

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 suggestionSelected = document.getElementsByClassName('pac-item-selected').length
          if (event.key === 'Enter' && !suggestionSelected) {
            const e = new KeyboardEvent('keydown', { 
              key: 'ArrowDown', 
              code: 'ArrowDown', 
              keyCode: 40, 
            })
            _listener.apply(input, [e])
          }
          _listener.apply(input, [event])
        }
      }
      _addEventListener.apply(input, [type, listener])
    }

    input.addEventListener = addEventListenerWrapper
  }

你应该很高兴去。本质上,该函数会捕获input字段中的每个按键,如果是enter,则模拟down-arrow按键。它还存储和重新绑定侦听器和事件以维护您的 Google 地图的所有功能Autocomplete()

感谢之前对大部分代码的回答,特别是 amirnissim 和 Alexander Schwarzman。

一开始这对我不起作用。我改变了模拟的 keydown 事件的创建方式,并且该解决方案很有魅力:const e = new KeyboardEvent("keydown", { key: "ArrowDown", code: "ArrowDown", keyCode: 40 });
2021-03-21 05:06:22
您忘记将 enableEnterKey 声明为函数。function enableEnterKey(input) {}除此之外,它完美无缺,谢谢!
2021-03-23 05:06:22
好点@Shadrix,我已经删除了 IE8-only attachEvent.
2021-03-27 05:06:22
通过添加一个范围,ieif (event.which >= 9 && event.which <= 13 && !suggestion_selected) {
2021-04-01 05:06:22
@Shadrix 现在也更新为现代浏览器密钥标识符。
2021-04-04 05:06:22

这是一个真实的、非 hacky 的解决方案的例子。它不使用任何浏览器黑客等,只是来自 Google 提供的公共 API 的方法,并在此处记录:Google Maps API

唯一的缺点是,如果用户未从列表中选择项目,则需要向 Google 提出额外请求。好处是结果总是正确的,因为查询的执行方式与 AutoComplete 中的查询相同。第二个好处是,通过只使用公共 API 方法而不依赖于 AutoComplete 小部件的内部 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);
    }
});
有没有人找到一种方法来使用此实现更新自动完成对象的“位置”?目前,如果使用它会autocomplete.getPlace()导致undefined.
2021-03-13 05:06:22
要回答 Ulad 的问题,只需将AutocompleteService代码移到'place_changed' 事件处理程序之外,而是设置状态,如果用户选择一个值,'place_changed' 将设置为 true。如果状态未按预期更改(我在输入字段上使用 on bur),则将AutocompleteServicewith 输入作为搜索键触发然而,我发现使用该服务的主要问题是不能保证返回的结果与完成 API 相同或顺序相同,因此您必须使用服务结果实现自己的预测 UI
2021-03-27 05:06:22
您的解决方案不正确,因为它在place_changed未触发事件时中断那是没有选择任何选项的时候。这意味着当用户按下 Enter 或 Tab 键,甚至单击选项中的其他位置时。
2021-04-10 05:06:22

似乎有一个更好更干净的解决方案:使用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

它的工作原理与Autocomplete按 Enter完全相同,因此...它不起作用。
2021-03-28 05:06:22
需要注意的是,您不能为 SearchBox 设置 componentRestrictions 选项。因此,如果您需要将建议限制在特定国家/地区,这不是替代方案。
2021-04-12 05:06:22