Google지도 Places API V3 자동 완성-입력시 첫 번째 옵션 선택
http://code.google.com/intl/sk-SK/apis/maps/documentation/javascript/places.html#places_autocomplete에 따라 입력 상자에 Google Maps Places V3 자동 완성 기능을 성공적으로 구현했습니다 . 잘 작동하지만 사용자가 Enter 키를 누를 때 제안에서 첫 번째 옵션을 선택하는 방법을 알고 싶습니다. 나는 JS 마술이 필요할 것 같지만 JS에 매우 익숙하고 어디서부터 시작 해야할지 모르겠습니다.
미리 감사드립니다!
최근에 작업 한 사이트에서 자동 완성을 구현할 때 동일한 문제가 발생했습니다. 이것이 내가 생각해 낸 해결책입니다.
$("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자동 완성 필드 내부를 누를 때마다 키 누르기를 시뮬레이션 합니다. ↓전과 이벤트는 트리거 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>
다음은 해킹되지 않은 실제 솔루션의 예입니다. 브라우저 해킹 등을 사용하지 않고 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);
}
});
다음은 2019 년에 대한 실제적인 답변입니다.
이것은이 페이지의 베스트 답변을 결합하고 순수한 JS 만 사용하며 간단한 ES6로 작성되었습니다. jQuery, 두 번째 API 요청 또는 IIFE가 필요하지 않습니다.
아이디어는 down-arrowamirnissim의 대답과 유사한 자동 완성 필드 내부에서 사용자가 리턴을 누를 때마다 ↓ ( ) 키 누르기 를 시뮬레이션하는 것입니다.
먼저 다음과 같이 주소 필드를 식별하도록 설정하십시오.
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 = 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
}
가셔도 좋습니다. 기본적으로이 함수는 input필드의 각 키 누르기를 캡처 하고 인 경우 enter대신 down-arrow키 누르기를 시뮬레이션합니다 . 또한 리스너와 이벤트를 저장하고 리 바인딩하여 Google지도의 모든 기능을 유지합니다 Autocomplete().
이 코드의 대부분, 특히 amirnissim 및 Alexander Schwarzman에 대한 이전 답변 덕분에 분명합니다.
훨씬 더 좋고 깨끗한 솔루션이있는 것 같습니다 . google.maps.places.SearchBox대신 google.maps.places.Autocomplete. 코드는 거의 동일하며 여러 곳에서 첫 번째 코드를 가져옵니다. Enter 키를 누르면 올바른 목록이 반환되므로 상자에서 벗어나 해킹이 필요하지 않습니다.
예제 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를 사용하여 이벤트 (해키)를 모방하거나 자동 완성 결과와 항상 일치하지 않는 Geocoder 또는 Google Places 검색 상자 를 사용했기 때문 입니다. 대신 여기에 설명 된대로 자바 스크립트 (jquery 없음)만으로 Google의 자동 완성 서비스를 사용합니다.
다음은 기본 Google API를 사용하여 자동 완성 상자를 생성 한 다음 쿼리를 다시 실행하여 첫 번째 옵션을 선택하는 가장 크로스 브라우저 호환 솔루션입니다.
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&language=en"></script>
자바 스크립트
// 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>
유일한 단점은 정보가 동일하더라도 기본 구현이 다른 데이터 구조를 반환한다는 것입니다. 그에 따라 조정하십시오.
amirnissim
의 답변에 대한 작은 개선 사항을 작성하고 싶습니다 . 게시 된 스크립트는 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/
이 답변 중 어느 것도 나를 위해 작동하지 않는 것 같습니다. 그들은 일반적인 위치를 얻었지만 실제로 내가 검색 한 실제 장소로 이동하지는 않습니다. .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 센트를 내놓을 것이라고 생각했습니다.
이건 어때?
$("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;
});
귀하의 모든 답변에 대해 완벽하게 작동하는 솔루션을 만들었습니다.
/**
* 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>
누군가에게 도움이되기를 바랍니다. 최선의 방법에 대해 자유롭게 토론하십시오.
@benregn @amirnissim 선택 오류의 원인은 다음과 같습니다.
var suggestion_selected = $(".pac-item.pac-selected").length > 0;
클래스 pac-selected는이어야합니다 pac-item-selected. 이는 !suggestion_selected항상 true로 평가되는 이유를 설명 하여 'keyup'또는 'keydown'을 사용하여 원하는 위치를 강조 표시 한 후 Enter 키를 누를 때 잘못된 위치가 선택되도록합니다.
이 문제를 해결하고 이제 angular js 및 angular Autocomplete 모듈을 사용하여 Google placces에서 첫 번째 옵션을 강제로 선택할 수 있습니다. 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의 대답 , 난 크로스 브라우저 방식 (amimissim의 솔루션은 IE8에서 작동하지 않는 것)에서 이벤트를 처리하는 구글의 API를 활용, 약간의 대안을 제시한다.
결과 div 클래스가 변경된 것처럼 변경 pac-item.pac-selected해야 pac-item-refresh.pac-selected했습니다. 이렇게하면 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]);
});
위대한 amirnissim 솔루션의 순수한 자바 스크립트 버전 (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]);
};
나는 같은 문제가 있기 때문에 이것을 약간 조사했습니다. 이전 솔루션에서 마음에 들지 않은 점은 자동 완성 기능이 예측을 표시하기 위해 이미 AutocompleteService를 실행했다는 것입니다. 따라서 예측은 어딘가에 있어야하며 다시로드되지 않아야합니다.
나는 장소 inkl의 예측을 알아 냈다. place_id 는
Autocomplete.gm_accessors_.place.Kc.l
그리고 당신은 기록에서 많은 데이터를 얻을 수 있습니다 [0].data. Imho , 주소 데이터 대신 place_id 를 사용하여 위치를 얻는 것이 더 빠르고 좋습니다 . 이 매우 이상한 객체 선택은 나에게별로 좋지 않은 것 같습니다.
자동 완성에서 첫 번째 예측을 검색하는 더 좋은 방법이 있는지 알고 계십니까?
사용자가 매번 잘못된 탐색을 트리거하지 않고 키보드로 목록 아래로 탐색하기 시작했는지 수신하는 작업 솔루션
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;
}
});
@Alexander 의 솔루션은 내가 찾고 있던 솔루션입니다. 그러나 그것은 오류를 일으켰습니다 TypeError: a.stopPropagation is not a function.
그래서 저는 KeyboardEvent. 작동 코드는 다음과 같습니다. Javascript 버전은 React.js 프로젝트에 매우 편리합니다. 나는 또한 이것을 React.js 프로젝트에 사용했습니다.
(function selectFirst(input) {
let _addEventListener = input.addEventListener
? input.addEventListener
: input.attachEvent;
function addEventListenerWrapper(type, listener) {
if (type === 'keydown') {
console.log('keydown');
let orig_listener = listener;
listener = event => {
let suggestion_selected =
document.getElementsByClassName('pac-item-selected').length > 0;
if (event.keyCode === 13 && !suggestion_selected) {
let simulated_downarrow = new KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
keyCode: 40
});
orig_listener.apply(input, [simulated_downarrow]);
}
orig_listener.apply(input, [event]);
};
}
_addEventListener.apply(input, [type, listener]);
}
if (input.addEventListener) input.addEventListener = addEventListenerWrapper;
else if (input.attachEvent) input.attachEvent = addEventListenerWrapper;
})(addressInput);
this.autocomplete = new window.google.maps.places.Autocomplete(addressInput, options);
이것이 누군가를 도울 수 있기를 바랍니다. :)
'programing tip' 카테고리의 다른 글
| 작업 복사본 잠김 (0) | 2020.11.06 |
|---|---|
| 플롯을 PDF로 저장 (0) | 2020.11.06 |
| System.Security.SecurityException : 원본을 찾을 수 없지만 일부 또는 모든 이벤트 로그를 검색 할 수 없습니다. (0) | 2020.11.06 |
| 산업 표준에서 #define이 금지되어 있습니까? (0) | 2020.11.06 |
| SearchView를 자동으로 확장하고 포커스를 제공합니다. (0) | 2020.11.06 |