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

Dan*_*ezo 68 javascript google-maps autocomplete google-maps-api-3

根据http://code.google.com/intl/sk-SK/apis/maps/documentation/javascript/places.html#places_autocomplete,我已成功在输入框中实施了Google Maps Places V3自动填充功能.它工作得很好,但我很想知道如何在用户按下回车时从建议中选择第一个选项.我想我需要一些JS魔法,但我对JS很新,不知道从哪里开始.

提前致谢!

ami*_*sim 163

这是一个解决方案,不会产生可能返回错误结果的地理编码请求: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>
Run Code Online (Sandbox Code Playgroud)

  • 好的解决方案,谢谢.这是这个问题的正确答案.如果可以的话,我会投票两次. (9认同)
  • 这个解决方案比接受的解决方案好得多,我建议重新接受它 (5认同)
  • 这个解决方案有一个小问题.如果用户使用向下箭头键实际选择列表中的第一个结果,则选择下面的结果 - 在这种情况下这是错误的... (4认同)
  • @benregn更改为`var suggestion_selected = $(".pac-item-selected").length> 0;` (4认同)
  • 你救了我的一天,非常感谢! (2认同)
  • 完美的解决方案。正是我想要的。在每种情况下均可工作。这应该被接受。 (2认同)

小智 44

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

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

    });
});
Run Code Online (Sandbox Code Playgroud)

http://jsfiddle.net/dodger/pbbhH/

  • 此方法的问题是它不会返回与第一个自动完成结果相同的地址.它在第一个自动完成结果的显示地址上执行地理编码,该地址可能是完全不同的地址.例如,输入"JFK"的第一个自动完成的结果是正确的地址(JFK通路,纽约,纽约州,美国),但地址"肯尼迪国际机场,纽约,纽约"(这是显示的地址解析结果第一个自动完成的地址将产生一个似乎是酒店的地址(144-02 135th Ave,Queens,NY 11436,USA). (14认同)

小智 21

这是一个真实的,非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);
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 你的解决方案不正确,因为它没有触发`place_changed`事件.那是没有选择任何选项的时候.这意味着当用户按下输入或制表键或甚至单击选项以外的其他位置时. (2认同)

Ton*_*nas 20

这是2018年的工作答案.

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

首先,假设您已经设置了类似的内容来识别您的地址字段:

const field = document.getElementById('address-field') 
const autoComplete = new google.maps.places.Autocomplete(field)
autoComplete.setTypes(['address'])
Run Code Online (Sandbox Code Playgroud)

然后在下一行添加:

enableEnterKey(field)
Run Code Online (Sandbox Code Playgroud)

然后在您的脚本中的其他地方,为了保持此功能在代码中保持良好且独立,请添加以下内容:

  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
  }
Run Code Online (Sandbox Code Playgroud)

你应该好好去.本质上,该down-arrow功能捕获input字段中的每个返回/输入按键,并模拟向下箭头按键.它还存储和重新绑定侦听器和事件,以维护您的Google地图的所有功能enter.

明显感谢大多数代码的早期答案,特别是amirnissim和Alexander Schwarzman.

  • 一开始这对我不起作用。我改变了模拟 keydown 事件的创建方式,并且该解决方案发挥了作用: `const e = new KeyboardEvent("keydown", { key: "ArrowDown", code: "ArrowDown", keyCode: 40 });` (5认同)
  • @Shadrix 现在也更新为现代浏览器密钥标识符。 (2认同)

Klo*_*ies 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);
  }
});
Run Code Online (Sandbox Code Playgroud)

该示例的完整源代码位于:https://gist.github.com/klokan/8408394

  • 应该注意,您无法为SearchBox设置componentRestrictions选项.因此,如果您需要将建议限制在特定国家/地区,则这不是替代方案. (5认同)
  • 它与"自动完成"完全相同,用于按Enter键,因此它不起作用. (3认同)

Ada*_*uld 9

对于Google Places Autocomplete V3,最佳解决方案是两个API请求.

这是小提琴

之所以没有其他答案足够的原因是因为他们要么使用jquery来模仿事件(hacky),要么使用Geocoder或Google Places Search框,它们并不总是匹配自动完成结果.相反,我们要做的是使用谷歌的自动完成服务,这里只详细说明javascript(没有jquery)

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

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&language=en"></script>
Run Code Online (Sandbox Code Playgroud)

使用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 ***** //
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

唯一的抱怨是本机实现返回不同的数据结构,尽管信息是相同的.相应调整.