我已经成功地在我的输入框上实现了Google Maps Places V3自动完成功能,具体方法请参考http://web.archive.org/web/20120225114154/http://code.google.com:80/intl/sk-SK/apis/maps/documentation/javascript/places.html。它的功能非常好,但是我想知道如何让它在用户按下回车键时选择建议列表中的第一个选项。我猜我需要一些JS代码的魔法,但我很新手,不知道从哪里开始。
我已经成功地在我的输入框上实现了Google Maps Places V3自动完成功能,具体方法请参考http://web.archive.org/web/20120225114154/http://code.google.com:80/intl/sk-SK/apis/maps/documentation/javascript/places.html。它的功能非常好,但是我想知道如何让它在用户按下回车键时选择建议列表中的第一个选项。我猜我需要一些JS代码的魔法,但我很新手,不知道从哪里开始。
这里有一个解决方案,它不会进行可能返回不正确结果的地理编码请求:http://jsfiddle.net/amirnissim/2D6HW/
每当用户在自动完成字段中按下return
键时,它模拟了一个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>
最近我在一个网站上实现自动完成时遇到了同样的问题。以下是我想出的解决方案:
$("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");
}
});
});
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 + " ");
这解决了问题。 - Christian Martinez2020年可行的解决方案。
我将此页面上最佳答案结合起来,用简单的ES6语法编写。无需jQuery、第二个API请求或IIFE。
基本上,每当用户在自动完成字段内按回车键时,我们模拟一个↓(下箭头)按键。
首先,假设您的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
}
你应该准备就绪了。实质上,该函数捕获字段中的每个按键,并且如果是键,则模拟按下一个键。它还存储并重新绑定监听器和事件,以维护Google Maps Autocomplete()
的所有功能。
特别感谢amirnissim和Alexander Schwarzman提供的早期答案。
function enableEnterKey(input) {}
除此之外,它完美无缺,谢谢! - richardjconst e = new KeyboardEvent("keydown", { key: "ArrowDown", code: "ArrowDown", keyCode: 40 });
。 - sebbabvar 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);
}
});
place_changed
事件未触发时它会出现问题。也就是说,当用户选择了“无”选项,或者按下回车键、Tab键,甚至在选项之外点击时,都会出现这种情况。 - saboteroautocomplete.getPlace()
将会返回undefined
。 - Ulad KasachAutocompleteService
代码移出“place_changed”事件处理程序,并设置状态,如果用户选择一个值,则将其设置为true。如果在预期时状态没有改变(我正在使用输入字段上的on bur),则使用输入作为搜索关键字触发AutocompleteService
。然而,我发现使用该服务的主要问题是返回的结果不能保证与完成API相同或按相同顺序排列,因此您必须使用服务结果实现自己的预测UI。 - verboze看起来有一个更好、更清晰的解决方案: 使用 google.maps.places.SearchBox
替代 google.maps.places.Autocomplete
。
代码几乎相同,只需获取多个地点中的第一个。按下 Enter 键后会返回正确的列表 - 所以它可以直接使用,没有必要进行任何 hack。
请参阅示例 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 API生成自动完成框,然后重新运行查询以选择第一个选项,这是最兼容跨浏览器的解决方案。
<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>
AutocompleteService
API 是个不错的选择。我通常会和 blur
事件一起使用,以便考虑到移动用户的操作。 - JoshGoogle.maps.places.PlacesService
上进行另一个 getDetails()
调用。 - Tdy关于你们所有的答案,我已经创建了一个完美适合我的解决方案。
/**
* 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>
希望这能对某人有所帮助。请随意讨论最佳实现方式。
这些答案似乎都对我无效。它们只能得到大致位置,但不能实际移动到我搜索的确切位置。在.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);
}
});
}
我知道这个问题已经有答案了,但是为了防止其他人像我一样遇到同样的问题,我也想发表一下我的看法。
$("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;
});
我只是想为amirnissim的答案写一个小增强。发布的脚本不支持IE8,因为在IE8中“event.which”似乎总是为空。
要解决这个问题,您只需要另外检查“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/