Liquid, JavaScript, themes, sales channels
Hey there, I am working on a custom theme. My problem is I want to show product price - cart total = discount amount on cart popup. So when I added product to cart it doesn't show the discount amount untill I refresh the page. After refreshing the page cart popup shows the discount amount. What I want is to refresh the cart popup when product is added without refreshing the whole page.
Is there any js for that? Like if product is added to cart refresh the cart popup.
{% if settings.language_switcher_enabled %}
<div class="translation-lab-language-switcher {{ settings.language_switcher_position }}">
<div class="translation-lab-dropdown">
{% if settings.language_switcher_flags %}
<img class="language-flag" src="{{ 'tlab-flags-' | append: request.locale.iso_code | append: '.png' | asset_url }}">
{% endif %}
</div>
<ul class="translation-lab-language-options {{ settings.language_switcher_position }}">
{%- for locale in shop.published_locales -%}
<li class="language-option {% if routes.root_url == locale.root_url %}active{% endif %}" data-language-code="{{ locale.iso_code }}">
{% if settings.language_switcher_flags %}
<img class="language-flag" src="{{ 'tlab-flags-' | append: locale.iso_code | append: '.png' | asset_url }}">
{% endif %}
{% case settings.language_switcher_label %}
{% when 'language-name' %}
<span class="language-label language-name">{{ locale.endonym_name | split: '(' | first | strip }}</span>
{% when 'language-code' %}
<span class="language-label language-code">{{ locale.iso_code | split: '-' | first | strip }}</span>
{% endcase %}
</li>
{%- endfor %}
</ul>
</div>
{% if request.design_mode %}
{% endif %}
{{ 'translation-lab-language-switcher-floating.css' | asset_url | stylesheet_tag }}
<style>
.translation-lab-language-switcher {
background-color: {{ settings.language_switcher_background_color }} !important;
border-color: {{ settings.language_switcher_border_color }} !important;
border-width: {{ settings.language_switcher_border_width }}px !important;
font-size: {{ settings.language_switcher_font_size }} !important;
font-weight: {{ settings.language_switcher_font_weight }} !important;
}
.translation-lab-dropdown {
color: {{ settings.language_switcher_text_color }} !important;
}
.translation-lab-dropdown:after {
border-color: {{ settings.language_switcher_text_color }} !important;
}
.translation-lab-language-switcher.top-right {
position: absolute;
top: {{ settings.language_switcher_spacing_top }}px;
bottom: auto;
left: auto;
right: {{ settings.language_switcher_spacing_right }}px;
}
.translation-lab-language-switcher.top-left {
position: absolute;
top: {{ settings.language_switcher_spacing_top }}px;
bottom: auto;
left: {{ settings.language_switcher_spacing_left }}px;
right: auto;
}
.translation-lab-language-switcher.bottom-left {
position: fixed !important;
top: auto !important;
bottom: {{ settings.language_switcher_spacing_bottom }}px !important;
left: {{ settings.language_switcher_spacing_left }}px !important;
right: auto !important;
}
.translation-lab-language-switcher.bottom-right {
position: fixed !important;
top: auto !important;
bottom: {{ settings.language_switcher_spacing_bottom }}px !important;
left: auto !important;
right: {{ settings.language_switcher_spacing_right }}px !important;
}
{% if settings.language_switcher_custom_css != blank -%}
{{- settings.language_switcher_custom_css -}}
{%- endif %}
</style>
<script>
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, < kValue, k, O >)).
// d. If testResult is true, return kValue.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}
// 7. Return undefined.
return undefined;
},
configurable: true,
writable: true
});
}
(function () {
function buildLanguageList() {
var languageListJson = '[{%- for locale in shop.published_locales %}{\
"name": "{{locale.name | split: "(" | first | strip}}",\
"endonym_name": "{{ locale.endonym_name | split: "(" | first | strip}}",\
"iso_code": "{{ locale.iso_code }}",\
"primary": {{ locale.primary }},\
"flag": "{{ locale.iso_code | append: ".png" | asset_url }}"\
}{% unless forloop.last %},{% endunless -%}{%- endfor %}]';
return JSON.parse(languageListJson);
}
function createDropdown(container, languageChangeHandler) {
var dropdown = container.children[0];
var span = dropdown.children[0];
var selectedImg = dropdown.children[1];
var ul = container.children[1];
for (var i = 0; i < ul.children.length; i++) {
var el = ul.children[i];
el.onclick = languageChangeHandler(el.dataset.languageCode, el.children[0].innerText, languageChangeCallback);
}
var isOpen = false;
dropdown.addEventListener('click', function (event) {
event.preventDefault();
isOpen = !isOpen;
if (isOpen) {
openDropdown();
} else {
closeDropdown();
}
});
container.addEventListener('mouseleave', function () {
closeDropdown();
});
function languageChangeCallback(lang, elementText) {
closeDropdown();
span.textContent = elementText;
if (selectedImg) {
selectedImg.src=lang.flag;
}
}
function openDropdown() {
isOpen = true;
dropdown.classList.add('open');
ul.classList.add('open');
}
function closeDropdown() {
isOpen = false;
dropdown.classList.remove('open');
ul.classList.remove('open');
}
}
function redirectUrlBuilderFunction(primaryLocale) {
var shopDomain = 'https://{{shop.domain}}';
if (window.Shopify.designMode) {
shopDomain = 'https://{{shop.permanent_domain}}';
}
var currentLocale = '{{request.locale.iso_code}}'.toLowerCase();
var currentLocaleRegEx = new RegExp('^\/' + currentLocale, "ig");
var primaryLocaleLower = primaryLocale.toLowerCase();
var pathname = window.location.pathname;
var queryString = window.location.search || '';
return function build(redirectLocale) {
if (!redirectLocale) {
return null;
}
var redirectLocaleLower = redirectLocale.toLowerCase();
if (currentLocale !== redirectLocaleLower) {
if (redirectLocaleLower === primaryLocaleLower) {
return shopDomain + pathname.replace(currentLocaleRegEx, '') + queryString;
} else if (primaryLocaleLower === currentLocale) {
return shopDomain + '/' + redirectLocaleLower + pathname + queryString;
} else {
return shopDomain + '/' + pathname.replace(currentLocaleRegEx, redirectLocaleLower) + queryString;
}
}
return null;
}
}
function configure() {
var languageList = buildLanguageList();
var primaryLanguage = languageList.find(function (x) { return x.primary; });
if (!primaryLanguage && !primaryLanguage.iso_code) {
// error: there should be atleast one language set as primary
return;
}
var redirectUrlBuilder = redirectUrlBuilderFunction(primaryLanguage.iso_code);
var containers = document.querySelectorAll('.translation-lab-language-switcher');
if (containers && containers.length) {
for (var i = 0; i < containers.length; i++) {
createDropdown(containers[i], languageChangeHandler);
}
}
function languageChangeHandler(languageCode, elementText, callback) {
var selectedLanguage = languageList.find(function (language) { return language.iso_code.toLowerCase() === languageCode.toLowerCase() });
return function () {
callback(selectedLanguage, elementText);
localStorage.setItem('translation-lab-lang', selectedLanguage.iso_code);
var redirectUrl = redirectUrlBuilder(selectedLanguage.iso_code);
redirectUrl && window.location.assign(redirectUrl);
}
}
}
configure();
})();
</script>
{% endif %}
{% include 'product-grid-variables', ratio:'1:1' %}
<div id="cart" class="btn-group btn-block">
<button type="button" data-toggle="dropdown" data-loading-text="{{ 'products.product.loading' | t }}" class="btn dropdown-toggle">
<span id="cart-total">
<span class="txt_number">{{ cart.item_count }}</span>
<span class="txt_items">{{ 'cart.general.my_cart'| t }}</span>
<span class="total-price">{{ item.line_price | money }}</span>
</span>
</button>
<ul class="dropdown-menu pull-right">
<li class="has-scroll">
<table class="table">
<tbody>
{% if cart.item_count < 1 %}
<p class="text-center cart-empty">{{ 'cart.general.no_items'| t }}</p>
{% else %}
{% for item in cart.items %}
<tr>
<td class="text-center">
<a href="{{ item.url }}">
{% include 'product-grid-image',image:item.image, customclass:'cart-image' %}
</a>
</td>
<td class="text-left info-item">
<a class="cart-name" href="{{ item.url }}">{{ item.product.title }}</a>
<p class="cart-quantity">{{ item.quantity }} ×</p>
<p class="cart-price">{{ item.original_price | money }}</p>
</td>
<td class="text-center cart-close">
<button type="button" onclick="cart.remove('{{ item.variant_id }}');" title="Remove" class="btn btn-danger btn-xs">
<i class="fa fa-angle-trash-alt"></i>
</button>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</li>
<li {% if cart.item_count < 1 %} class="hide"{% endif %}>
<table class="table">
<tr>
<td class="text-left">{{ 'cart.general.subtotal' | t }} :</td>
<td class="text-right" id="cart-discount"> {{ cart.total_discount | money }}</td>
<td class="text-right" id="cart-subtotal"> {{ cart.total_price | money }}</td>
</tr>
</table>
<p class="text-center cart-button">
<a href="/checkout">{{ 'cart.general.checkout' | t }}</a>
</p>
</li>
</ul>
</div>
And this is my ajax cart
// product page add to cart
$('#button-cart').on('click', function(e) {
e.preventDefault();
$.ajax({
url: '/cart/add.js',
type: 'post',
data: $('#form_buy input[type=\'text\'], #form_buy select'),
dataType: 'json',
beforeSend: function() {
$('#button-cart').button('loading');
},
complete: function() {
$('#button-cart').button('reset');
},
success: function(json) {
$('.alert, .text-danger').remove();
$('.form-group').removeClass('has-error');
if (json['error']) {
if (json['error']['option']) {
for (i in json['error']['option']) {
var element = $('#input-option' + i.replace('_', '-'));
if (element.parent().hasClass('input-group')) {
element.parent().after('<div class="text-danger">' + json['error']['option'][i] + '</div>');
} else {
element.after('<div class="text-danger">' + json['error']['option'][i] + '</div>');
}
}
}
if (json['error']['recurring']) {
$('select[name=\'recurring_id\']').after('<div class="text-danger">' + json['error']['recurring'] + '</div>');
}
// Highlight any found errors
$('.text-danger').parent().addClass('has-error');
}
toastr.success('{{ 'cart.general.item_added' | t }}');
$.getJSON('/cart.js', function(cart) {
load(cart);
buildMiniCart();
});
$('html, body').animate({ scrollTop: 0 }, 'slow');
},
error: function(xhr, ajaxOptions, thrownError) {
if(xhr.status == 422 ) {
toastr.warning('{{ 'cart.general.item_added_402' | t }}');
} else {
alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
}
}
});
});
Hello, PamDigital!
I've dealt with cartjs and cart updates. I can work with you to make the updates automatic without the update button.
Your update will have to be triggered recursively via a callback. If you'd like, I can modify and license a proprietary js function class for use.
Please message me for more information.
Are you ready to take your business to the next level? Look no further than the latest ...
By SarahF_Shopify Apr 15, 2024We’re keeping the ball rolling to make sure you’re always ahead of the game. So buckle ...
By JasonH Apr 8, 2024Portrait of Stephen positioned next to an image of planet Earth, with the Stephen's World ...
By JasonH Mar 18, 2024