Skip to content

Instantly share code, notes, and snippets.

@ihorduchenko
Last active May 18, 2023 21:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ihorduchenko/1ec35aba5ab232f1ac3aaf673751f1b4 to your computer and use it in GitHub Desktop.
Save ihorduchenko/1ec35aba5ab232f1ac3aaf673751f1b4 to your computer and use it in GitHub Desktop.
Product upsells with async Add to cart functionality
<div class="pdp-upsells--panel-row">
{%- for prod in metafield -%}
{%- assign first_variant = prod.variants[0] -%}
{%- assign visibility_class = '' -%}
<div class="pdp-upsells--cell">
<div class="pdp-upsells--item">
<div class="pdp-upsells--item-image img-contain">
{%- if prod.featured_image != blank -%}
<img
loading="lazy"
src="{{ prod.featured_image | img_url: 'master' }}"
alt="{{ prod.featured_image.alt }}">
{%- endif -%}
</div>
<h3 class="pdp-upsells--item-title heading h6">
{{ prod.title }}
</h3>
{%- for item in cart.items -%}
{%- if item.variant_id == first_variant.id -%}
{% assign visibility_class = ' added' %}
{%- endif -%}
{%- endfor -%}
<div class="pdp-upsells--item-actions atcAddDynamicEl{{ visibility_class }}">
<button
data-variant-id="{{ first_variant.id }}"
data-quantity="1"
class="button button--primary pdp-upsells--item-add flex-1 atcDynamicBtnAdd">
<span class="pdp-upsells--item-text-added">
{{ 'product.form.added_to_cart' | t }}
</span>
<span class="pdp-upsells--item-text-def">
{{ 'product.form.add_to_cart_short' | t }}
</span>
</button>
<button
class="button button--primary pdp-upsells--item-remove atcAddDynamicBtnRmv"
data-variant-id="{{ first_variant.id }}">
{%- render 'icon', icon: 'trash-can' -%}
</button>
</div>
</div>
</div>
{%- endfor -%}
</div>
<style>
.pdp-upsells--item-actions.added .pdp-upsells--item-remove {
display: flex;
}
.pdp-upsells--item-actions.added .pdp-upsells--item-add {
pointer-events: none;
opacity: 0.7;
}
.pdp-upsells--item-remove {
display: none;
}
.pdp-upsells--item-text-added {
display: none;
}
.pdp-upsells--item-actions.added .pdp-upsells--item-text-def {
display: none;
}
.pdp-upsells--item-actions.added .pdp-upsells--item-text-added {
display: inline;
}
</style>
<script>
const getCart = () => {
return fetch(window.Shopify.routes.root + 'cart.js')
.then(response => response.json());
}
function addItemToCart(id, qty, cback) {
let formData = {
items: [{
'id': id,
'quantity': qty
}]
};
fetch('/cart/add.js', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(formData)
})
.then(response => {
cback();
return response.json();
})
.catch((error) => {
console.error('Error:', error);
});
}
function updateItemInCart(id, qty, cback) {
let formData = {
updates: {
[id]: qty
}
};
fetch('/cart/update.js', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(formData)
})
.then(response => {
cback();
return response.json();
})
.catch((error) => {
console.error('Error:', error);
});
}
atcAddDynamicEl.forEach(function(el) {
let btnAdd = el.querySelector('.atcDynamicBtnAdd');
let btnRmv = el.querySelector('.atcAddDynamicBtnRmv');
function onCartChanged() {
getCart().then(data => {
let cartItemsCount = document.querySelectorAll('.header__cart-count');
cartItemsCount && cartItemsCount.forEach((cntr) => {
cntr.innerHTML = data.item_count;
})
});
}
function onAddedToCart() {
el.classList.add('added');
onCartChanged();
}
function onRemovedFromCart() {
el.classList.remove('added');
onCartChanged();
}
btnAdd.addEventListener('click', function(e) {
let varId = btnAdd.dataset.variantId;
let varQty = btnAdd.dataset.quantity;
addItemToCart(varId, varQty, onAddedToCart);
});
btnRmv.addEventListener('click', function(e) {
let varId = btnAdd.dataset.variantId;
updateItemInCart(varId, 0, onRemovedFromCart);
});
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment