How do I add a "place_change" event listener in a separate callback as I cannot edit the built in BigCommerce callback, I want to be able to fire the event listener on selection of an autocomplete option without instantiating a Map or an autocomplete object on an input as there is already one generated by the BigCommerce built in callback.
I have to write custom JavaScript code in the script manager in BigCommerce to add the Suburb address component to Google places autocomplete. I am doing this by using the mutation observer to detect when the shipping address form components are loaded into the DOM on the one step checkout and this works well.
What my script is doing is:
- setting a pattern for HTML5 form validation to make sure the address selected must start with a number and case insensitive which includes letters A to Z, numbers (0-9) and hyphen "-" and forward slash "/" as well as Maori accented characters for Maori vowels ā, ē, ī, ō, ū
- Adds Suburb to autocomplete, as there is no way for me to amend the BigCommerce code for adding an address component to Google autocomplete, I need to write my own code to pass the address selected from autocomplete and return Suburb to auto populate my custom field Suburb on the shipping form
I have had issues setting the correct event listener to fire at the right time, I ended up using the blur event and then setting a 1 second timer, although this works, it does feel very hacky and I would prefer to use the "place_changed" event instead, I am just not sure how to set an event listener for this outside of the callback for autocomplete, which I don't have access to. I wrote my own callback, but not sure how to use "place_change" event without loading a map or invoking autocomplete on an input.
Trying to simulate and test concepts in JSFiddle
Dirty timer method from JSFiddle
...
// Invoke autocomplete custom listener via JavaScript initAutocomplete_custom();
document.addEventListener('DOMContentLoaded', function() {
var js_file = document.createElement('script');
js_file.type = 'text/javascript';
js_file.src = 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry&callback=initAutocomplete_custom';
document.getElementsByTagName('head')[0].appendChild(js_file);
});
...
...
//custom call back to geometry library
function initAutocomplete_custom() {
// When the user selects an address from the dropdown, populate the address
// fields in the form.
const input = document.getElementById('addressLine1Input');
input.addEventListener('blur', (e) => {
getAddressComponent_test();
});
}
...
...
function getAddressComponent_test() {
document.getElementById("sublocality_level_1").value = '';
document.getElementById("sublocality_level_1").disabled = false;
var delayInMilliseconds = 1000; //1 second
setTimeout(function() {
//your code to be executed after 1 second
var address = document.getElementById("addressLine1Input").value;
var geocoder = new google.maps.Geocoder();
geocoder.geocode({
'address': address
}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0].address_components[2].short_name !== undefined){
var suburb = results[0].address_components[2].short_name;
document.getElementById("sublocality_level_1").value = suburb;
console.log(suburb);
}
} else {
console.log("Invalid Address");
}
});
}, delayInMilliseconds);
}
...
My BigCommerce code from the script manager
<script>
(function(win) {
'use strict';
var listeners = [],
doc = win.document,
MutationObserver = win.MutationObserver || win.WebKitMutationObserver,
observer;
function ready(selector, fn) {
// Store the selector and callback to be monitored
listeners.push({
selector: selector,
fn: fn
});
if (!observer) {
// Watch for changes in the document
observer = new MutationObserver(check);
observer.observe(doc.documentElement, {
childList: true,
subtree: true
});
}
// Check if the element is currently in the DOM
check();
}
function check() {
// Check the DOM for elements matching a stored selector
for (var i = 0, len = listeners.length, listener, elements; i < len; i++) {
listener = listeners[i];
// Query for elements matching the specified selector
elements = doc.querySelectorAll(listener.selector);
for (var j = 0, jLen = elements.length, element; j < jLen; j++) {
element = elements[j];
// Make sure the callback isn't invoked with the
// same element more than once
if (!element.ready) {
element.ready = true;
// Invoke the callback with the element
listener.fn.call(element, element);
}
}
}
}
// Expose `ready`
win.ready = ready;
})(this);
ready('#checkoutShippingAddress', function(element) {
// Hit checkoutShippingAddress console flag
console.log("You're on the shipping step!");
// Invoke autocomplete custom listener via JavaScript initAutocomplete_custom(); must be done via JavaScript
document.addEventListener('DOMContentLoaded', function () {
var js_file = document.createElement('script');
js_file.type = 'text/javascript';
js_file.src = 'https://maps.googleapis.com/maps/api/js?key=<API_key_placeholder>&libraries=geometry&callback=initAutocomplete_custom';
document.getElementsByTagName('head')[0].appendChild(js_file);
});
/** @start HTML5 form validation **/
// Target autocomplete form input
let fulladdress = document.getElementById('addressLine1Input');
// Address validation must start with a number, case insensitive which includes letters A to Z, numbers (0-9), hyphen "-", forward slash "/" as well as Maori accented characters for Maori vowels ā, ē, ī, ō, ū
fulladdress.setAttribute("pattern", "\\d[/a-zA-ZĀ-ū0-9\\s',-]*");
// event listener to clear error message for input
fulladdress.addEventListener('input', () => {
fulladdress.setCustomValidity('');
fulladdress.checkValidity();
});
// event listener to invoke validation and show error message if needed
fulladdress.addEventListener('invalid', () => {
fulladdress.setCustomValidity('No PO Box or Private Bag, address must start with a number, e.g. 1/311 Canaveral Drive');
});
/** @end HTML5 form validation **/
function initAutocomplete_custom() {
// When the user selects an address from the dropdown, populate the address
// fields in the form.
const input = document.getElementById('addressLine1Input');
input.addEventListener('blur', (e) => {
getAddressComponent_test();
});
}
function getAddressComponent_test() {
var delayInMilliseconds = 1000; //1 second
setTimeout(function() {
var address = document.getElementById("addressLine1Input").value;
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status){
if (status==google.maps.GeocoderStatus.OK){
if (results[0].address_components[2].short_name !== undefined){
var suburb = results[0].address_components[2].short_name;
document.getElementById("addressLine2Input").value = suburb;
console.log(suburb);
}
} else{ console.log("Invalid Address"); }
});
}, delayInMilliseconds);
}
});
</script>