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


72

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

提前致谢!


好问题。我今天在想这个。我唯一的担心是强迫用户执行他们可能不想做的任何事情。一些大型网站并不强制选择第一个选项,因为有多个选项可见,它们仅在仅显示一个选项时才强制选择,这对我来说似乎更好。
luke_mclachlan 2015年

Answers:


46

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

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


15
此方法的问题在于它没有返回与第一个自动完成结果相同的地址。它会在第一个自动完成结果的显示地址上执行地址解析,该地址可能是完全不同的地址。例如,输入“ jfk”的第一个自动完成结果是正确的地址(JFK Access Road,纽约,纽约,美国),但是地址为“ JFK Airport,纽约,纽约”的地理编码结果(即显示)第一个自动完成的地址)将产生看似酒店的地址(美国纽约州皇后区144-02 135th Ave,纽约州11436)。
Reed G. Law

1
就我而言,我做了以下工作: 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 + " ");
这就

您不能只对第一个项目执行.click()来触发与您相同的处理。
安东尼·安德里亚

你能告诉我,我怎么能“的.pac项查询”后加上的空间,因为现在当我点击输入空格被删除
山姆

谢谢!谢谢!!谢谢!!!我不知道您可以使用Geocoder.geocode()来获取address_components。您已经节省了我的工作时间!再次感谢你!
山姆

170

这是一种不发出可能会返回错误结果的地理编码请求的解决方案: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>

9
好的解决方案,谢谢。这是对这个问题的正确答案。如果可以的话,我将投票两次。
Matt B

4
此解决方案有一个小问题。如果用户实际上使用向下箭头键选择列表中的第一个结果,则选择以下结果-在这种情况下,这是错误的结果–
benregn

4
@benregn更改为var suggestion_selected = $(".pac-item-selected").length > 0;
JaroslavŠtreit,2014年

2
您救了我的一天,万分感谢!
jarandaf

5
这个解决方案要好得多然后接受的解决方案,我建议重新接受它
约翰·F·

24

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 Maps的所有功能Autocomplete()

在此之前,对于大多数代码,尤其是amirnissim和Alexander Schwarzman,都给予了感谢。


1
您忘记将enableEnterKey声明为一个函数。function enableEnterKey(input) {}除此之外,它还可以完美运行,谢谢!
richardj '18年

1
通过添加范围也进行了调整,以处理选项卡键,即ifif(event.which> = 9 && event.which <= 13 &&!suggestion_selected){
Isabelle

1
@Shadrix好点,我已经删除了IE8-only attachEvent
托尼·布拉索纳斯

1
@Shadrix现在也更新为现代浏览器密钥标识符。
Tony Brasunas

5
一开始这对我不起作用。我更改了const e = new KeyboardEvent("keydown", { key: "ArrowDown", code: "ArrowDown", keyCode: 40 });
模拟的

22

这是一个真实的,非骇客的解决方案的示例。它不使用任何浏览器黑客等工具,仅使用Google提供的公共API中的方法,并在此处进行了记录:Google Maps API

唯一的缺点是,如果用户未从列表中选择一项,则需要向Google提出其他请求。好的一面是,结果将始终是正确的,因为查询的执行方式与自动完成内的查询相同。第二个好处是,仅使用公共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);
    }
});

2
您的解决方案不正确,因为它在place_changed事件未触发时会中断。那就是当没有选择任何选项时。这意味着当用户按下Enter键或Tab键,甚至单击某个选项之外的其他位置时。
sabotero 2015年

有没有人找到这种方法来更新自动完成对象的“位置”?当前,如果使用,将autocomplete.getPlace()导致undefined
乌拉德·卡萨赫

要回答Ulad的问题,只需将AutocompleteService代码移到“ place_changed”事件处理程序之外,然后将状态设置为:如果用户选择一个值,则“ place_changed”将设置为true。如果状态在预期时没有发生变化(我在输入字段上使用bur),则触发AutocompleteServicewith输入作为搜索键。但是,我发现使用该服务的主要问题是,不能保证返回的结果与完成API相同或相同,因此您必须使用服务结果实现自己的预测UI
verboze

12

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


4
它的工作原理与Autocomplete按Enter完全相同,因此...不起作用。
klimat

6
请注意,您不能为SearchBox设置componentRestrictions选项。因此,如果您需要将建议限制在特定的国家/地区,则这不是替代方案。
Felix Wienberg

9

对于Google Places Autocomplete V3,最好的解决方案是两个API请求。

这是小提琴

其他答案都不满足的原因是,他们要么使用jquery来模仿事件(骇客),要么使用了Geocoder或Google Places Search框,但这些并不总是与自动填充结果匹配。相反,我们要做的是使用Google的自动完成服务,此处仅使用javascript(没有jquery)进行了详细说明

下面详细介绍了最兼容跨浏览器的解决方案,使用本机Google API生成自动完成框,然后重新运行查询以选择第一个选项。

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&language=en"></script>

Java脚本

// 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>

唯一的麻烦是,尽管信息相同,但本机实现会返回不同的数据结构。相应地调整。


良好的AutocompleteServiceAPI调用。我已与blur事件结合使用,以说明移动用户的窃听。
2015年

1
这是这个问题上最干净,最可靠的答案。如果您需要像GPS坐标地址一些更多的细节(并得到相同的对象自动完成),您可以拨打另一个电话,以getDetails()Google.maps.places.PlacesService
TDY

只是警告:getDetails非常昂贵。额外的金钱成本是自动完成本身的6倍。
巴特

3

关于您的所有答案,我创建了一个非常适合我的解决方案。

/**
 * 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>

希望它能对某人有所帮助。请随时讨论最好的方法。


2

我只是想为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小提琴:http//jsfiddle.net/QW59W/107/


2

这些答案似乎都不适合我。他们会获得大致的位置,但实际上不会平移到我搜索的实际位置。在.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

这个怎么样?

$("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;
});

1

@benregn @amirnissim我认为选择错误来自:

var suggestion_selected = $(".pac-item.pac-selected").length > 0;

该类pac-selected应为pac-item-selected,这说明了为什么!suggestion_selected总是求值为true,从而导致在使用'keyup'或'keydown'突出显示所需位置后,按Enter键时会选择错误的位置。


1

我做了一些工作,现在我可以使用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>

这是一个傻瓜
谢谢你。


1

工作解决方案,侦听用户是否已开始使用键盘向下浏览列表,而不是每次都触发错误的导航

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
这是具有可读代码和原始JavaScript的唯一实际解决方案。谢谢卡拉姆!
Maarten de Graaf

0

amimissim的答案的基础上,我提出了一个替代方案,利用Google的API以跨浏览器的方式处理事件(amimissim的解决方案在IE8中似乎不起作用)。

我也不得不改变pac-item.pac-selected,以pac-item-refresh.pac-selected像它看起来的结果DIV类已更改。这样就可以按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]);
});

0

只是伟大的amirnissim解决方案的纯JavaScript版本(无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]);
    };

1
小错误在这里。getElementsByClassName('.pac-item-selected')应该是getElementsByClassName('pac-item-selected')
Tony Brasunas

感谢您的JavaScript版本。这是我一直在寻找的那个。但这引起了TypeError: a.stopPropagation is not a function错误
Roman M.

0

由于存在相同的问题,因此我对此进行了调查。我对以前的解决方案不满意的是,自动完成功能已经触发了自动完成服务以显示预测。因此,预测应该在某处并且不应再次加载。

我发现墨迹的预测。place_id存储在

Autocomplete.gm_accessors_.place.Kc.l

您将能够从记录中获取大量数据[0].data。恕我直言,使用place_id而不是地址数据来获取位置更快,更好。对我来说,这种非常奇怪的对象选择似乎不太好。

您是否知道是否有更好的方法可以从自动完成中检索第一个预测?


1
确实非常危险,因为当前字段名称已更改。目前,中有一系列预测对象autocomplete.gm_accessors_.place.Zc.predictions,其中autocomplete是自动完成对象。预测对象具有人类可读的键。我希望有一个记录在案的访问器可以访问此数组。也许有。
巴特

0

@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);

希望这可以帮助某人,:)


0
    /// <reference types="@types/googlemaps" />
import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Inject, Input, NgZone, OnInit, Output, ViewChild} from '@angular/core';
import {MapsAPILoader, MouseEvent} from '@agm/core';
import { Address } from 'src/@core/interfaces/address.model';
import { NotificationService } from 'src/@core/services/notification.service';
// import {} from 'googlemaps';
declare var google: any;

// @ts-ignore

@Component({
  selector: 'app-search-address',
  templateUrl: './search-address.component.html',
  styleUrls: ['./search-address.component.scss']
})
export class SearchAddressComponent implements OnInit {

  @Input('label') label: string;
  @Input('addressObj') addressObj: Address = {};
  zoom: number;
  isSnazzyInfoWindowOpened = false;

  private geoCoder;

  // @ts-ignore
  @Output() onAddressSelected = new EventEmitter<any>();
  @Input('defaultAddress') defaultAddress = '';
  @ViewChild('search', {static: true})
  public searchElementRef: ElementRef = null;


  constructor(
    private mapsAPILoader: MapsAPILoader,
    private ngZone: NgZone,
    private notify: NotificationService,
    @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef
  ) { }


  ngOnInit() {
    // console.log('addressObj# ', this.addressObj);
    if (this.defaultAddress !== '') {
      this.searchElementRef.nativeElement.value = this.defaultAddress;
    }
    // load Places Autocomplete
    this.mapsAPILoader.load().then(() => {
      if (this.addressObj.address) {
        this.setZoom();
      } else {
        this.setCurrentLocation();
      }
      this.geoCoder = new google.maps.Geocoder;
      const autocomplete = new google.maps.places.Autocomplete(this.searchElementRef.nativeElement, {
        types: ['address']
      });
      autocomplete.setTypes(['(cities)']);
      autocomplete.setComponentRestrictions({'country': 'in'});
      autocomplete.addListener('place_changed', () => {
        this.ngZone.run(() => {
          // get the place result
          const place: google.maps.places.PlaceResult = autocomplete.getPlace();

          // verify result
          if (place.geometry === undefined || place.geometry === null) {
            return;
          }

          // set latitude, longitude and zoom
          this.addressObj.latitude = place.geometry.location.lat();
          this.addressObj.longitude = place.geometry.location.lng();
          this.getAddress(this.addressObj.latitude, this.addressObj.longitude);
          this.zoom = 12;
        });
      });
    });
  }

  setZoom() {
    this.zoom = 8;
  }

  // Get Current Location Coordinates
  private setCurrentLocation() {
    if ('geolocation' in navigator) {
      navigator.geolocation.getCurrentPosition((position) => {
        this.addressObj.latitude = position.coords.latitude;
        this.addressObj.longitude = position.coords.longitude;
        this.zoom = 8;
        this.getAddress(this.addressObj.latitude, this.addressObj.longitude);
      });
    }
  }

  markerDragEnd($event: MouseEvent) {
    this.addressObj.latitude = $event.coords.lat;
    this.addressObj.longitude = $event.coords.lng;
    this.getAddress(this.addressObj.latitude, this.addressObj.longitude);
  }

  getAddress(latitude, longitude) {
    this.addressObj.latitude = latitude;
    this.addressObj.longitude = longitude;
    this.geoCoder.geocode({ location: { lat: latitude, lng: longitude } }, (results, status) => {
      if (status === 'OK') {
        if (results[0]) {
          console.log('results ', results);
          this.zoom = 12;
          this.addressObj.address = results[0].formatted_address;
          this.showSnazzyInfoWindow();
          this.addressObj.placeId = results[0].place_id;

          for(let i = 0; i < results[0].address_components.length; i++) {
            if (results[0].address_components[i].types[0] == 'locality') {
              this.addressObj.city = results[0].address_components[i].long_name;
            }
            if (results[0].address_components[i].types[0] == 'administrative_area_level_1') {
              this.addressObj.region = results[0].address_components[i].long_name;
            }
            if (results[0].address_components[i].types[0] == 'country') {
              this.addressObj.country = results[0].address_components[i].long_name;
            }
            if (results[0].address_components[i].types[0] == 'postal_code') {
              this.addressObj.zip = results[0].address_components[i].long_name;
            }
          }

          this.transmitData();
        } else {
          this.notify.showMessage('No results found', 3000, 'OK');
        }
      } else {
        this.notify.showMessage('Google maps location failed due to: ' + status, 3000, 'OK');
      }

    });
  }


  transmitData() {
   // console.log(this.addressObj);
    this.onAddressSelected.emit(this.addressObj);
  }

  toggleSnazzyInfoWindow() {
    this.isSnazzyInfoWindowOpened = !this.isSnazzyInfoWindowOpened;
  }

  showSnazzyInfoWindow() {
    this.isSnazzyInfoWindowOpened = true;
  }

}




<mat-form-field class="full-width pt-2 flex-auto w-full">
  <input matInput [(ngModel)]="addressObj.address" type="text" (keydown.enter)="$event.preventDefault()" placeholder="{{label ? label : 'Location'}}" autocorrect="off" autocapitalize="off" spellcheck="off" type="text" #search>
</mat-form-field>

<agm-map
  [latitude]="addressObj.latitude"
  [longitude]="addressObj.longitude"
  [zoom]="zoom">
  <agm-marker
    [latitude]="addressObj.latitude"
    [longitude]="addressObj.longitude"
    [markerDraggable]="true"
    (dragEnd)="markerDragEnd($event)">
  </agm-marker>

</agm-map>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.