Reputation: 16993
I have an HTML form input element that uses a SearchBox
from the Google Maps Places API for auto-completion. In my JavaScript I have created a SearchBox
instance as follows:
var input = $('#spotsearch')[0]; // Refers to a form input with type="text" in my HTML
searchBox = new google.maps.places.SearchBox(input);
Later in in my JavaScript, I have the following:
$('#searchform').submit(function () {
var places = searchBox.getPlaces();
if (places) {
var spotLat = searchBox.getPlaces()[0].geometry.location.lat();
var spotLng = searchBox.getPlaces()[0].geometry.location.lng();
$('#latitude').val(spotLat);
$('#longitude').val(spotLng);
}
});
Here is my issue: after selecting a completion candidate, when I submit the form by clicking the form's submit button, everything works as expected, and places
will reference an array with a single object. However, if I submit the form by pressing ENTER
, the anonymous function still gets called, but places returns an empty array. In both cases, it seems that the request being sent is exactly the same. What am I missing here?
I am new to JavaScript and to the Goggle Maps API, so I am not sure where my mistake originates. My intuition is that it may be related to timing in some way, but that is really just a vague hunch. Any suggestions would be appreciated. Thank you!
To provide a bit more detail, here is my entire script:
var searchBox;
var places;
function initSearchBox() {
var KCLatLng = new google.maps.LatLng({lat: 39.093201, lng: -94.573419});
var bounds = new google.maps.LatLngBounds(KCLatLng);
var input = $('#spotsearch')[0];
searchBox = new google.maps.places.SearchBox(input);
searchBox.setBounds(bounds);
$('#searchform').submit(function () {
var places = searchBox.getPlaces();
if (places) {
var spotLat = searchBox.getPlaces()[0].geometry.location.lat();
var spotLng = searchBox.getPlaces()[0].geometry.location.lng();
$('#latitude').val(spotLat);
$('#longitude').val(spotLng);
}
});
}
initSearchBox
is then passed as the callback
parameter when I load the Goggle Maps API.
I have also tried doing it this way:
var searchBox;
var places;
function initSearchBox() {
var KCLatLng = new google.maps.LatLng({lat: 39.093201, lng: -94.573419});
var bounds = new google.maps.LatLngBounds(KCLatLng);
var input = $('#spotsearch')[0];
searchBox = new google.maps.places.SearchBox(input);
searchBox.setBounds(bounds);
searchBox.addListener('places_changed', function() {
var places = searchBox.getPlaces();
if (places) {
var spotLat = searchBox.getPlaces()[0].geometry.location.lat();
var spotLng = searchBox.getPlaces()[0].geometry.location.lng();
$('#latitude').val(spotLat);
$('#longitude').val(spotLng);
}
});
}
But this gives me the same result (hitting ENTER
does not set the value of #latitude
or #longitude
, but clicking the submit button does).
Here is the HTML of my form (using Django templates and Bootstrap):
<form method="get" id="searchform" action="{% url 'spot_search' %}"
class="navbar-form navbar-left plain" role="search">
<div class="form-group">
<input id="spotsearch" type="text" class="form-control plain"
placeholder="Search spots" name="search_query">
</div>
<button type="submit" class="btn btn-default plain">
<span class="glyphicon glyphicon-search"></span> Search
</button>
<input id="latitude" name="latitude" type="hidden">
<input id="longitude" name="longitude" type="hidden">
</form>
Upvotes: 0
Views: 1049
Reputation: 16993
Thanks @adam0101 for pointing me to this question. I also found the related questions here, here and here to be useful. I missed them in my first pass at researching the issue, because the issue was more specific to the Google Maps autocomplete API than I had realized.
While I considered marking my question as duplicate, I have decided to instead post the solution that I ended up going with, which is heavily inspired by this highly upvoted - but not accepted - answer. I simply added the following to my existing JavaScript:
$('#spotsearch').keydown(function (e) {
// Don't submit form unless values have been set
var condition = $('#latitude').val() == "" && $('#longitude').val() == "";
if (e.which == 13 && condition) {
return false;
}
});
This way, the first time the user presses enter on an autocompletion candidate it will select that candidate (instead of submitting the form before the candidate can be selected). Then the user can either hit enter again to submit the form, or click the Search
button to do the same thing. The solution it is modeled after is this one:
$('#inputId').keydown(function (e) {
if (e.which == 13 && $('.pac-container:visible').length) return false;
});
from the answer linked above. I modified this slightly so that it does not depend on the SearchBox
having a particular class defined by Google and out of my control.
Upvotes: 1
Reputation: 30995
Is this getting submitted to a server? If so, it's probably a race condition. The form isn't waiting for the hidden fields to get set before submitting to the server. Regardless of whether you set the values from within the form submit event or the places_changed
event of the SearchBox, you will still need to either prevent the enter key from submitting the form to the server or prevent the form from being submitted via user action so you can programmatically submit it later when your data is set.
To disable the enter key from submitting the form, look here: Google Autocomplete - enter to select
To disable the form submission, do as Juan suggested and include e.preventDefault();
Upvotes: 1
Reputation: 1048
I think that reason why the submit event handler is triggered when you press the enter button, but without places as an empty array is because the event handler is executed before than the initSearchBox function. It is possible that you previosly executed the initSearchBox so you had attached the submit event handler.
My recommendations is to replace your code by this one:
$('#searchform').submit(function (e) {
var KCLatLng = new google.maps.LatLng({lat: 39.093201, lng: -94.573419});
var bounds = new google.maps.LatLngBounds(KCLatLng);
var input = $('#spotsearch')[0];
var searchBox = new google.maps.places.SearchBox(input);
searchBox.setBounds(bounds);
var places = searchBox.getPlaces();
if (places) {
var spotLat = searchBox.getPlaces()[0].geometry.location.lat();
var spotLng = searchBox.getPlaces()[0].geometry.location.lng();
$('#latitude').val(spotLat);
$('#longitude').val(spotLng);
}
// Prevent to perform a "real" submit (so page is not reloaded or redirected)
e.preventDefault();
});
In this way all the entire search code is going to be executed when the form is submitted.
Upvotes: 0