I am using the JavaScript Google places autocomplete API v3. It works fine but I was wondering if there was a way to force the selection from the autocomplete, that is input that will not accept any free form text. I looked at the docs and didn't see such an option but figured I would ask just to be safe. I'm sure I could work out a way to do it with some JavaScript but would prefer to use an already built method if it is available. Thanks!
The Google Places API does not currently support this feature. If you believe this would be a useful feature please submit a Places API - Feature Request.
Actually, what we did was the following:
- Every time a location is picked from the Auto complete list, we populate some hidden fields with some fields coming from the JSON response (city name, country name, longitude and latitude)
- When the form is submitted, we check if these fields have values, if they don't, it means that the user instead of selecting a location from the list, he just entered a value himself.
It is not solving the problem in a JS way, but still, it does the trick just fine!
<script src="http://maps.googleapis.com/maps/api/js?sensor=false&libraries=places"
type="text/javascript"></script>
<script>
var IsplaceChange = false;
$(document).ready(function () {
var input = document.getElementById('txtlocation');
var autocomplete = new google.maps.places.Autocomplete(input, { types: ['(cities)'] });
google.maps.event.addListener(autocomplete, 'place_changed', function () {
var place = autocomplete.getPlace();
IsplaceChange = true;
});
$("#txtlocation").keydown(function () {
IsplaceChange = false;
});
$("#btnsubmit").click(function () {
if (IsplaceChange == false) {
$("#txtlocation").val('');
alert("please Enter valid location");
}
else {
alert($("#txtlocation").val());
}
});
});
</script>
Enter
the place_changed
event will be thrown and the get place object will be returned with a single property name with the text that you entered. –
Paulenepauletta The Google Places API does not currently support this feature. If you believe this would be a useful feature please submit a Places API - Feature Request.
The following solution did the trick for me (with jQuery). The idea is to use the blur()
event to force the last selected address once the input loses focus.
We add a timeout to prevent a conflict between the blur
and the place_changed
events.
Consider the following html:
<input type="text" placeholder="Your address" id="autocomplete">
And the following javascript :
var autocomplete = new google.maps.places.Autocomplete(
(document.getElementById('autocomplete')),
{types: ['geocode']});
autocomplete.addListener('place_changed', fillInAddress);
var currentAddress = {
line_1: "",
line_2: "",
zipcode: "",
city: "",
country: "",
lat: "",
lng: "",
one_liner: ""
};
function fillInAddress() {
var place = this.autocomplete.getPlace();
// reset the address
currentAddress = {
line_1: "",
line_2: "",
zipcode: "",
city: "",
country: "",
lat: "",
lng: "",
one_liner: place.formatted_address
};
// store relevant info in currentAddress
var results = place.address_components.reduce(function(prev, current) {
prev[current.types[0]] = current['long_name'];
return prev;
}, {})
if (results.hasOwnProperty('route')) {
currentAddress.line_1 = results.route;
}
if (results.hasOwnProperty('street_number')) {
currentAddress.line_1 = results.street_number + " " + currentAddress.line_1;
}
if (results.hasOwnProperty('postal_code')) {
currentAddress.zipcode = results.postal_code;
}
if (results.hasOwnProperty('locality')) {
currentAddress.city = results.locality;
}
if (results.hasOwnProperty('country')) {
currentAddress.country = results.country;
}
currentAddress.lat = Number(place.geometry.location.lat()).toFixed(6);
currentAddress.lng = Number(place.geometry.location.lng()).toFixed(6);
}
$('#autocomplete').blur(function() {
var address = $('#autocomplete').val();
// we set a timeout to prevent conflicts between blur and place_changed events
var timeout = setTimeout(function() {
// release timeout
clearTimeout(timeout);
if (address !== currentAddress.one_liner) {
$('#autocomplete').val(currentAddress.one_liner);
}
}, 500);
});
I hope this helps.
var options = {
language: 'en',
types: ['(cities)'],
};
var input = document.getElementById('city');
var autocomplete = new google.maps.places.Autocomplete(input, options);
input.addEventListener("change", function(){
input.value = "";
});
Quiting the value when the input changes works for me. It forces to use Google Places.
I was having the same problem, below is a function that I came up with as a work-around. This could be used in combination with an event such as onchange or onkeyup. Hope it helps!
//Arguments:
address - string containing the place that you want to validate
address_success_div - div object specifically used for displaying validation
function is_location_valid(address, address_success_div)
{
var geocoder = new google.maps.Geocoder();
geocoder.geocode( {"address": address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK)
{
address_success_div.innerHTML = "SUCCESS";
}
else
{
address_success_div.innerHTML = "FAILURE";
}
});
}
You can use the input pattern tag, forcing the user to choose a predeterminate based on Google Autocomplete API.
The input:
<form>
<input type="text" onkeyup="changePatterns()" onchange="changePatterns()" id="autocomplete" required pattern="">
<input type="submit">
</form>
The JS file:
function changePatterns() {
let input = document.getElementById('autocomplete').value;
let regExp = new RegExp(' ', 'g');
input = input.replace(regExp, '%20');
let mapsURL = 'https://maps.googleapis.com/maps/api/place/autocomplete/json'
mapsURL += '?types=geocode';
mapsURL += '&key=YOUR-KEY-HERE';
mapsURL += `&input=${input}`;
const endpoint = 'middle.php';
let formData = new FormData();
formData.append('url', mapsURL);
fetch(endpoint, {
method: 'post',
body: formData
})
.then(blob => blob.json())
.then(response => {
const predictions = response.predictions;
let {length} = predictions;
let pattern = '';
for(let i = 0; i < length; i++){
pattern += i == 0 ? '' : '|';
pattern += predictions[i].description
}
const input = document.getElementById('autocomplete');
input.setAttribute('pattern', pattern);
});
}
I had to use a php intermediate because of problems with CORS policy:
<?php
header('Content-Type: application/json');
$url = $_POST['url'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //remove on upload
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_AUTOREFERER, false);
curl_setopt($ch, CURLOPT_REFERER, "https://www.notmydomain.com");
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$result = curl_exec($ch);
echo curl_error($ch);
curl_close($ch);
echo $result;
Just in case someone gets into this post and is using React with Google Places Autocomplete, this worked for me:
const [place, setPlace] = React.useState(null);
const input = document.getElementById('google-places-address');
const handleBlur = () => {
if (!place) {
onChange('');
}
};
useEffect(() => {
if (input) {
input.addEventListener('blur', handleBlur);
}
return () => {
if (input) {
input.removeEventListener('blur', handleBlur);
}
};
}, [input]);
onPlaceSelected: (place) => {
setPlace(place);
(Your code here)
}
onChange is the function I get from React Hook Form controller, but should work with any input control system you use.
Basically when you click outside the autocomplete the input gets blurred and if no option from Google was selected the input gets cleaned. When you select an option, the input also gets cleaned but then filled with the onPlaceSelected function.
I solved the problem combining the suggestions on a couple of different answers from here. My Textfield where the user inputs the address is called txtPlaces. Here's my code:
var pac_input = document.getElementById('txtPlaces');
(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 || event.which == 9) && !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);
$('#txtPlaces').blur(function(e){
selectFirstAddress(pac_input);
});
////Ensuring that only Google Maps adresses are inputted
function selectFirstAddress (input) {
google.maps.event.trigger(input, 'keydown', {keyCode:40});
google.maps.event.trigger(input, 'keydown', {keyCode:13});
}
This will properly react to Enter or Tab keys, as well as when the user selects another field. Hope this helps!
I solved my issue using this code found here
<asp:TextBox runat="server" ID="TextBox1" />
<input type="button" id="btnSubmit" name="name" value="Validate" />
<div id="dvMap">
</div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&key=AIzaSyBE1J5Pe_GZXBR_x9TXOv6TU5vtCSmEPW4"></script>
<script type="text/javascript">
var isPlaceChanged = false;
google.maps.event.addDomListener(window, 'load', function () {
var places = new google.maps.places.Autocomplete(document.getElementById('<%= TextBox1.ClientID %>'));
google.maps.event.addListener(places, 'place_changed', function () {
isPlaceChanged = true;
var geocoder = new google.maps.Geocoder();
var place = places.getPlace();
var address = place.formatted_address;
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var latitude = results[0].geometry.location.lat();
var longitude = results[0].geometry.location.lng();
var mapOptions = { center: new google.maps.LatLng(latitude, longitude), zoom: 15, mapTypeId: google.maps.MapTypeId.ROADMAP };
var map = new google.maps.Map(document.getElementById("dvMap"), mapOptions);
var marker = new google.maps.Marker({ position: new google.maps.LatLng(latitude, longitude), map: map });
} else {
alert("Request failed.")
}
});
});
});
$(function () {
$("#TextBox1").keydown(function () {
isPlaceChanged = false;
});
$("#btnSubmit").click(function () {
if (!isPlaceChanged) {
$("#TextBox1").val('');
alert("Please Enter valid location");
}
else {
alert($("#TextBox1").val());
}
});
});
</script>
Like they mentioned it's not supported in the Google Autocomplete API. I found a way to make it work by creating a hidden element that stores the selected places than check if it's equal to the autocomplete input.
/*----------------------------------------------------*/
/* check if it's valid address
/*----------------------------------------------------*/
function checkAddress(location, validLocation) {
if (location.val() == validLocation.val()) {
return true;
}
return false;
}
/*----------------------------------------------------*/
/* initialize auto complete address
/*----------------------------------------------------*/
function initialize() {
let input = document.getElementById('autocomplete-input');
let inputHidden = document.getElementById('autocomplete-input-hidden');
let options = {
types: ['(regions)'],
componentRestrictions: {country: "tn"}
};
let autocomplete =new google.maps.places.Autocomplete(input, options);
autocomplete.setFields(['formatted_address']);
autocomplete.addListener('place_changed', function () {
inputHidden.value= autocomplete.getPlace().formatted_address;
});
}
call the function in your main.
let location = $('#autocomplete-input');
let validLocation = $('#autocomplete-input-hidden');
checkAddress(location,validLocation);
and in your html:
<input id="autocomplete-input" type="text">
<input id="autocomplete-input-hidden" type="hidden" >
I found a simpler solution that I implemented with jquery validation plugin, but that's not a must. I just used a class name as a flag to show whether the field was valid (i.e. the user made a selection from Google) or invalid (i.e. they typed their own stuff). I created two functions, one to add the class "err" and one to take it away.
function invalidate(element_id){
$('#' + element_id).addClass('err');
}
function makeValid(element_id){
if ($('#' + element_id).hasClass('err')){
$('#' + element_id).removeClass('err');
}
$('#' + element_id).valid(); //this is a method from jquery validation plugin to check validity of a field
}
Then, onkeydown I called the "invalidate" function to flag that the input element had been typed into.
document.getElementById("element_id").setAttribute('onkeydown', "invalidate('element_id')");
And at the end of the Google Autocomplete API (I did it at the end of the fillInAddress method), I called "makeValid" to remove the flag because the user has just chosen an option from the dropdown menu.
makeValid(userElementId);
Finally, I validated that the "err" class that I used as my flag for when the user typed instead of choosing an option is not present. Here I used jquery validation plugin, but you could use something else if desired.
jQuery.validator.addMethod("hasClassErr", function(value, element) {
return this.optional( element ) || !element.classList.contains("err");
}, jQuery.validator.format("Please choose an option from the dropdown menu."));
FORM_RULES = {
element_name: {required: true, 'hasClassErr': true}
};
$('#form_id').validate({
rules: FORM_RULES
});
see https://mcmap.net/q/336755/-events-other-than-39-place_changed-39-for-google-maps-autocomplete
Below doesn't work. just kept for history
An easier approach could be:
let placeInputEl = document.getElementById("placeInput");
let autcomplete = new google.maps.places.Autocomplete(placeInputEl);
placeInputEl.addEventListener("change", () => {
//this fires when the user changes to a non-selection / freeform text
doStuff(true);
});
autocomplete.addListener("place_changed", () => {
//this fires when the user selects a place from Google
doStuff(false);
});
function doStuff(isFreeText) {
//maybe clear the selection or do whatever you need to reset
if (isFreeText) {
placeInputEl.value = "";
return;
}
// handle a place selection
}
<input id="placeInput" />
this lets you clear the input, determine validation, reset the input, or whatever you want to do to make the selection mandatory.
the place_changed
event only fires when the user selects an input or hits enter.
the change
event fires before place_changed
so you may be able to apply your logic without interfering with google's api call
Hia I have managed to do that by creating the autocomplete object each time the user focuses the input field, and checking it's value each time it's blured:
var autocompleteField;
var input = document.getElementById('YOUR_INPUT');
$('#YOUR_INPUT').focus(function(e){
if(autocompleteField){
autocompleteField=null;
}
autocompleteField = new google.maps.places.Autocomplete(input);
});
$('#YOUR_INPUT').blur(function(e){
var results = autocompleteField.getPlace();
if(!results){
$(this).attr('value','');
}
});
© 2022 - 2024 — McMap. All rights reserved.