requires: jQuery
estimate-shipping-calculator.js
window.estimateShippingCalculator = window.estimateShippingCalculator || {};
(function ($) {
"use strict";
window.estimateShippingCalculator = {
init() {
$('[data-adev-estimate-shipping]:not(.adev-init)').each((index, el) => {
const self = $(el);
self.addClass('adev-init');
const calculate = self.find('[data-adev-es="calculate"]');
const countrySelect = self.find('[data-adev-es="country"]');
countrySelect.on('change', (e) => this.handleCountryChange(e, self));
calculate.on('click', (e) => this.handleCalculateClick(e, self));
});
},
createOptionElement(province) {
const option = document.createElement('option');
option.value = province[0];
option.innerHTML = province[1];
return option;
},
populateProvinces(provinces, provinceSelect) {
provinceSelect.empty();
provinces.forEach((province) => {
provinceSelect.append(this.createOptionElement(province));
});
},
handleCountryChange(e, self) {
const provinceField = self.find('[data-adev-es="province-field"]');
const provinceSelect = self.find('[data-adev-es="province"]');
self.find('[data-adev-es="result"]').hide();
const selectedCountryEl = $(e.target)[0];
const selectedOption = selectedCountryEl[selectedCountryEl.selectedIndex];
const provinces = JSON.parse(selectedOption.dataset.provinces || []);
if (provinces && provinces.length === 0) {
provinceField.hide();
} else {
this.populateProvinces(provinces, provinceSelect);
provinceField.show();
}
},
handleCalculateClick(e, self) {
const provinceField = self.find('[data-adev-es="province-field"]');
const countrySelect = self.find('[data-adev-es="country"]');
const provinceSelect = self.find('[data-adev-es="province"]');
const zip = self.find('[data-adev-es="zip"]');
const result = self.find('[data-adev-es="result"]');
e.preventDefault();
result.removeClass('alert-error alert-success').hide();
const params = `shipping_address[zip]=${zip.val()}&shipping_address[country]=${countrySelect.val()}&shipping_address[province]=${provinceSelect.val()}`;
$(e.currentTarget).addClass('loading');
$.ajax({
url: `/cart/shipping_rates.json?${params}`,
method: "GET",
dataType: 'json',
success: (data) => {
$(e.currentTarget).removeClass('loading');
this.displayShippingRates(result, data)
},
error: (error) => {
$(e.currentTarget).removeClass('loading');
this.displayErrors(result, error.responseJSON)
},
});
},
displayShippingRates(element, data) {
if (data.shipping_rates && data.shipping_rates.length) {
const headingLocaleStr = data.shipping_rates.length === 1 ? 'There is one shipping rate for this destination:' : 'There are multiple shipping rates for this destination:';
let rates = '';
data.shipping_rates.forEach((rate) => {
const formattedRate = theme.moneyFormat.replace(/\{\{\s*(\w+)\s*\}\}/, rate.price);
rates += `<li>${rate.name}: ${formattedRate}</li>`;
});
element.addClass('alert-success').html(`<p>${headingLocaleStr}</p><ul class="styled-list">${rates}</ul>`).show()
} else {
element.addClass('alert-success').html('<p>We do not ship to this destination.</p>').show()
}
},
displayErrors(element, data) {
let errors = '';
Object.keys(data).forEach((key) => {
errors += `<li>${data[key]}</li>`;
});
element.addClass('alert-error').html(`<ul class="styled-list">${errors}</ul>`).show();
}
};
document.addEventListener('adev:estimate:shipping:init', () => {
window.estimateShippingCalculator.init();
});
document.dispatchEvent(new CustomEvent('adev:estimate:shipping:init'));
})(jQuery);
estimate-shipping-calculator.liquid
<script src="{{ 'estimate-shipping-calculator.js' | asset_url }}" defer></script>
<div class="adev-estimate-shipping" data-adev-estimate-shipping>
<div class="adev-es__heading">Estimate Shipping</div>
<div class="adev-es__form">
<div class="adev-es__result alert" data-adev-es="result"></div>
<div class="adev-es__field">
<label for="adev-es-country">Country</label>
<select id="adev-es-country" data-adev-es="country" name="address[country]" data-default="{% if shop.customer_accounts_enabled and customer %}{{ customer.default_address.country }}{% endif %}">{{ country_option_tags }}</select>
</div>
<div class="adev-es__field" data-adev-es="province-field" style="display: none;">
<label for="adev-es-province">Province</label>
<select id="adev-es-province" data-adev-es="province" name="address[province]" data-default="{% if shop.customer_accounts_enabled and customer and customer.default_address.province != '' %}{{ customer.default_address.province }}{% endif %}"></select>
</div>
<div class="adev-es__field">
<label for="adev-es-zip">Zip Code</label>
<input type="text" id="adev-es-zip" data-adev-es="zip" name="address[zip]"{% if shop.customer_accounts_enabled and customer %} value="{{ customer.default_address.zip }}"{% endif %}>
</div>
<div class="adev-es__field maring-0">
<button type="button" class="btn block adev-es__calculate" data-adev-es="calculate">
<span>Calculate</span>
</button>
</div>
</div>
</div>