If you are having issues with setting element values for React rendered form elements, this will do the trick:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
//Example
setNativeValue(document.getElementById(componentInputs[addressType]), val);
document.getElementById(componentInputs[addressType]).dispatchEvent(new Event('input', { bubbles: true }));
function setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
}
<!-- end snippet -->
This works perfectly in Chrome, Edge, Firefox and Safari, hope it helps other people too, it also resolves the issue of how to programmatically fill input elements built with React.
The answer is simply turn off the BigCommerce Google Address AutoComplete and write your own. I hope this helps other people and saves them time.
Simply add the script to your BigCommerce Script Manager targeting the Checkout
```
<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);
var autocomplete;
var componentForm = {
subpremise: 'short_name',
street_number: 'short_name',
route: 'long_name',
sublocality_level_1: 'short_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
postal_code: 'short_name'
};
var componentInputs = {
sublocality_level_1: 'addressLine2Input',
locality: 'cityInput',
administrative_area_level_1: 'provinceInput',
postal_code: 'postCodeInput'
};
var regex = /^.*(po\s*box|private\s*bag).*$|^\d[\/a-zĀ-ū0-9\s\,\'\-]*$/i;
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** @type {!HTMLInputElement} */
(document.getElementById('addressLine1Input')), {
types: ['geocode']
});
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
// Break out out address components from autocomplete
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
var short_address = "";
var subprem = "";
document.getElementById('checkout-shipping-continue').disabled = 'disabled';
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
var str = document.getElementById("addressLine1Input").value;
var match = str.match(regex);
// Check if address type is in components we are looking for
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
// Set generic address components in form inputs
if (componentInputs[addressType]){
// document.getElementById(componentInputs[addressType]).value = val;
setNativeValue(document.getElementById(componentInputs[addressType]), val);
document.getElementById(componentInputs[addressType]).dispatchEvent(new Event('input', { bubbles: true }));
}
// Build short address string
if(addressType == 'street_number') {
short_address = val;
} else if (addressType == 'route') {
short_address += " " + val;
short_address = short_address.trim();
} else if (addressType == 'subpremise') {
if (val !== undefined) {
var numberPattern = /\d+/g;
var arr_match = val.match( numberPattern );
subprem = arr_match[0] + "/";
}
}
// Validate full address selected
if (match && !match[1]) {
// If address is valid set to short address after breaking out the components
// document.getElementById('addressLine1Input').value = (subprem + short_address);
var str_addr = subprem + short_address;
setNativeValue(document.getElementById('addressLine1Input'), str_addr);
document.getElementById('addressLine1Input').dispatchEvent(new Event('input', { bubbles: true }));
document.getElementById('addressLine1Input').focus();
document.getElementById('addressLine1Input').blur();
}
}
}
// Run validation rules and UI prompts
validate();
}
function validate() {
// Get address value for validation
var str = document.getElementById("addressLine1Input").value;
if (str.indexOf(',') > -1) {
var arr_str = str.split(',');
str = arr_str[0];
setNativeValue(document.getElementById('addressLine1Input'), str);
document.getElementById('addressLine1Input').dispatchEvent(new Event('input', { bubbles: true }));
}
var match = str.match(regex);
var lbl = document.getElementById('addressLine1Input-label');
var short_address = document.getElementById('addressLine1Input');
var shippingform = document.querySelectorAll('[data-form-type="shipping"]');
// If address is valid
if (match && !match[1]) {
if(shippingform.length > 0){
shippingform[0].setAttribute("onsubmit", "return validateMyForm(true);");
}
document.getElementById('checkout-shipping-continue').disabled = false;
short_address.setAttribute('title', 'Address appears to be valid');
short_address.setCustomValidity('');
if (lbl.style.removeProperty) {
lbl.style.removeProperty('color');
} else {
lbl.style.removeAttribute('color');
}
lbl.innerHTML = "Address (No PO Box)";
// else address is invalid
} else {
if(shippingform.length > 0){
shippingform[0].setAttribute("onsubmit", "return validateMyForm(false);");
}
document.getElementById('checkout-shipping-continue').disabled = 'disabled';
short_address.focus();
short_address.setCustomValidity('No PO Box or Private Bag address must start with a number, e.g. 1/311 Canaveral Drive');
short_address.setAttribute('title', 'No PO Box or Private Bag address must start with a number, e.g. 1/311 Canaveral Drive');
lbl.style.color = '#b22222';
lbl.innerHTML = "No PO Box or Private Bag address must start with a number, e.g. 1/311 Canaveral Drive";
}
}
function setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
}
function validateMyForm(answer){
return answer;
}
ready('#checkoutShippingAddress', function(element) {
// Invoke autocomplete custom listener via JavaScript initAutocomplete(); must be done via JavaScript
var lib = 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete';
if(!isLoadedScript(lib)){
var js_file = document.createElement('script');
js_file.type = 'text/javascript';
js_file.src = lib;
document.getElementsByTagName('head')[0].appendChild(js_file);
}
// Hit checkoutShippingAddress console flag
console.log("You're on the shipping step!");
/** @start HTML5 form validation **/
// Target autocomplete form input
let fulladdress = document.getElementById('addressLine1Input');
fulladdress.addEventListener('blur', () => {
validate();
});
// 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',-]*");
fulladdress.setAttribute("onblur", "validate()");
// 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 **/
// Detect if library loaded
function isLoadedScript(lib) {
var script = document.querySelectorAll('[src="' + lib + '"]');
if(script.length > 0){
script[0].remove();
}
return document.querySelectorAll('[src="' + lib + '"]').length > 0;
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
});
</script>
```