Google地点自动完成-按Enter键选择第一个结果?

37
我正在使用Google Places自动完成功能,当表单字段中存在建议时,我希望它只选择结果列表中的顶部项目,而不是其他任何项目,这与以前的问题有所不同: Google maps Places API V3自动完成-按Enter键选择第一个选项 Google maps Places API V3自动完成-按Enter键选择第一个选项(并保持不变) 但是,那些问题中的答案似乎实际上并不起作用,或者它们解决了特定的附加功能。
似乎像以下内容应该可以工作(但实际上并不行):
$("input#autocomplete").keydown(function(e) {
  if (e.which == 13) {          
    //if there are suggestions...
    if ($(".pac-container .pac-item").length) {
      //click on the first item in the list or simulate a down arrow key event
      //it does get this far, but I can't find a way to actually select the item
      $(".pac-container .pac-item:first").click();
    } else {
      //there are no suggestions
    }
  }
});

任何建议都将不胜感激!

2
你需要使用google.maps.places.AutocompleteService类并手动读取AutocompletePrediction:https://developers.google.com/maps/documentation/javascript/reference#AutocompleteService - Marcelo
6
@Mubix接受了你链接到的第二个问题中所涵盖的内容,看起来已经满足了需求,并额外添加了一些可以轻松剥离的附加功能。根据我刚刚从上述来源派生的演示,这样做就是:http://jsfiddle.net/Ut2U4/1/ - Craig Blagg
1
@CraigBlagg 谢谢!最终我搞定了。不确定为什么第一次没有成功,但是逐行重建它得到了期望的结果! - NChase
太好了。奇怪的是它没有立即起作用(在这里似乎可以工作)。但很高兴它能够解决问题@NChase - Craig Blagg
@CraigBlagg,在您的示例(jsfiddle.net/Ut2U4/1)中,需要确保在触发“focusout”时从文档中删除“keypress”事件(第14行)。否则,每次输入获得焦点时都会将其附加到文档中。 - Nik Sumeiko
9个回答

24
我已经阅读了这个问题的许多答案,以及链接问题答案的许多次数,最终发现最佳答案是这个(注:遗憾的是,它不是被接受的答案!)。
我修改了2或3行代码将其转换为可直接使用的函数,您可以将其复制/粘贴到您的代码中并适用于许多元素(如果需要)。以下是代码:
var selectFirstOnEnter = function(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]); // add the modified listener
    }
    if (input.addEventListener) { 
        input.addEventListener = addEventListenerWrapper; 
    } else if (input.attachEvent) { 
        input.attachEvent = addEventListenerWrapper; 
    }
}

使用方法:

selectFirstOnEnter(input1);
selectFirstOnEnter(input2);
...

1
这个运作得很好。它需要 jQuery。如果你像我一样在用 Angular,这里有一个小指南教你如何安装它:https://medium.com/@swarnakishore/how-to-include-and-use-jquery-in-angular-cli-project-592e0fe63176 - Mladen
2
请确保在 var autocomplete = new google.maps.places.Autocomplete(input); 之前调用该函数,否则它将无法正常工作。 - woodii
1
好的答案,代码格式很糟糕。不管怎样还是谢谢 :) - Derenir
1
@Derenir 已修复! - Basj

9

我重新发布了我的回答,源自于Google地图Places API V3自动完成 - 选择第一个选项:

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


19
搜索框不允许你按(地区)或(城市),或者任何类型进行筛选。 - metsfan
我测试了一下,结果并不如预期。当你输入时,SearchBox会执行“自动完成”请求,但是当你按下回车键时,它会使用你输入的字符串在https://maps.googleapis.com/maps/api/js/PlaceService.QueryPlaces上进行请求。这可能会返回实际结果,也可能不会。 例如,通过输入“naxo”,自动完成列表将返回“Naxos ke Mikres Kyklades”作为第一个结果(也许这是基于区域设置?)。如果没有选择列表项,按下回车键将返回0个结果。 - pkExec
搜索框非常方便。我真的希望它能让我限制结果为地理编码或地址。 - Adam Starrh
1
但搜索框与自动完成不同。它更适用于像“纽约的咖啡”这样的事情。 - user151496
我已经使用自动完成功能约一年了,但在阅读此文并切换到SearchBox后,一切都变得更好了。我之前使用了一个特殊的“getPlaceFromAutocompleteList”函数来解决问题,但这个新功能修复了几个令人沮丧的边缘情况错误。谢谢! - Kelderic
显示剩余2条评论

6

以下是重点内容,这是一个监听用户是否使用键盘向下导航列表的解决方案,而不是每次都触发错误导航。

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

});

1
这似乎会导致一个错误 TypeError: a.stopPropagation is not a function error,你对此有什么想法吗? - RobbTe

6

为了实现我网站上的相同功能,我需要使用jQuery模拟插件(https://github.com/jquery/jquery-simulate),然后附加事件:

$("input#autocomplete").focusin(function () {
    $(document).keypress(function (e) {
        if (e.which == 13) {
            $("input#autocomplete").trigger('focus');
            $("input#autocomplete").simulate('keydown', { keyCode: $.ui.keyCode.DOWN } ).simulate('keydown', { keyCode: $.ui.keyCode.ENTER });
        }
    });
});

该插件将模拟按下向下箭头键和回车键的操作,回车键本身不起作用,我找不到其他选择第一个选项的方法。

希望这可以帮助您。


1
在你使用箭头键进行导航并通过按回车键选择时,这种方法不起作用。 - dnlmzw
如果您使用箭头键进行导航,并使用回车键进行选择,则会触发“place_change”... - user151496
@user151496没错,但是如果你的字段是一个<form>字段,这种方法可能行不通,这时你需要在按下回车键(13/$.ui.keyCode.ENTER)的时候使用e.preventDefault();,否则服务器响应很可能来不及返回。 :) - jave.web
在经过数小时的搜索和尝试类似解决方案的自我破解后,这对我来说完美地运作了。 - Antony D'Andrea

4
这是我解决问题的最简单方法:

这是我解决问题的最简单方法:

autocomplete.addListener('place_changed', function() {
  if(event.keyCode == 13 || event.keyCode == 9) { // detect the enter key
    var firstValue = $(".pac-container .pac-item:first").text(); // assign to this variable the first string from the autocomplete dropdown
     }
    $('#search-address').val(firstValue); // add this string to input
    console.log(firstValue); // display the string on your browser console to check what it is
   //(...) add the rest of your code here
  });
}

我修改了上面的函数,现在它对我非常有效。//监听器将keydown设置为true并选择第一个搜索结果 this.searchElementRef.nativeElement.addEventListener('keydown', (e) => { if (e.keyCode === 13 || e.keyCode === 9) { var firstValue = $('.pac-container .pac-item:first').text(); // 如果用户没有使用箭头导航且尚未运行此操作 this.txtValue = firstValue; google.maps.event.trigger(e.target, 'keydown', { keyCode: 40, hasRanOnce: true, }); } }); - Techiemanu
这对我来说导致了一个错误。在此范围内,事件对象是什么?您没有在函数参数中提到它。 - Ali Samie

2
这是我所做的事情,它是有效的:

HTML:

<input name="location" id="autocomplete" autocomplete="off" type="text" class="textbx" placeholder="Enter Destination" required>

googleautocompletecustomized.js:

        function initialize() {
      // Create the autocomplete object, restricting the search
      // to geographical location types.
      if($('#autocomplete').length){
          autocomplete = new google.maps.places.Autocomplete(
              (document.getElementById('autocomplete')),
              {
                types: ['(regions)'],
                componentRestrictions: {country: "in"}
              });
          google.maps.event.addListener(autocomplete, 'place_changed', function() {
            $('#autocomplete').closest('form').data('changed', true);
            fillInAddress();
          });         
      }

    //select first result
        $('#autocomplete').keydown(function (e) {
            if (e.keyCode == 13 || e.keyCode == 9) {
                $(e.target).blur();
                if($(".pac-container .pac-item:first span:eq(3)").text() == "")
                    var firstResult = $(".pac-container .pac-item:first .pac-item-query").text();
                else
                    var firstResult = $(".pac-container .pac-item:first .pac-item-query").text() + ", " + $(".pac-container .pac-item:first span:eq(3)").text();

                var geocoder = new google.maps.Geocoder();
                geocoder.geocode({"address":firstResult }, function(results, status) {
                    if (status == google.maps.GeocoderStatus.OK) {
                        placeName = results[0];
                        e.target.value = firstResult;
                        fillInAddress(placeName);
                        $('#datetimepicker1 .input-group-addon').click();
                    }
                });
            }

        });
    }

// [START region_fillform]
function fillInAddress(place) {
  // Get the place details from the autocomplete object.
  if(!place)
    var place = autocomplete.getPlace();

  for (var component in componentForm) {
    document.getElementById(component).value = '';
    document.getElementById(component).disabled = false;
  }

  // Get each component of the address from the place details
  // and fill the corresponding field on the form.
  for (var i = 0; i < place.address_components.length; i++) {
    var addressType = place.address_components[i].types[0];
    if (componentForm[addressType]) {
      var val = place.address_components[i][componentForm[addressType]];
      document.getElementById(addressType).value = val;

    }
  }

}

0

-1
将输入元素放在表单元素外部。使用JavaScript填充表单。
document.getElementById("adress").value = place.formatted_address;

-1

我在这方面做了一些工作,现在我可以使用AngularJS和Angular Autocomplete模块强制选择Google Places的第一个选项。
感谢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
谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接