All things Shopify and commerce
Hello I added this cart to my cart-drawer.liquid and it works until I click a remove button or quantity button in my cart, which causes the cart to reset and the javascript not to be used anymore I think. URL: Glow Curtain: 400 LED Lights for a Magical Ambiance – InteriorGlows
<!-- Upsell Container -->
<div class="cart-upsell-module">
{% if settings.cart_upsell_products != blank %}
{% assign cart_product_ids = cart.items | map: 'product_id' %}
{% assign upsell_count = 0 %}
{% for product in settings.cart_upsell_products %}
{% unless cart_product_ids contains product.id %}
{% assign upsell_count = upsell_count | plus: 1 %}
{% endunless %}
{% endfor %}
{% if upsell_count > 0 %}
<div class="cart-upsell-wrapper">
<h3>Je kunt dit ook leuk vinden</h3>
{% if upsell_count > 1 %}
<div class="cart-upsell-nav-wrapper">
<div class="swiper cart-upsell-swiper">
<div class="swiper-wrapper">
{% for product in settings.cart_upsell_products %}
{% unless cart_product_ids contains product.id %}
<div class="swiper-slide">
<div class="upsell-inner" data-variant-id="{{ product.variants.first.id }}">
<div class="upsell-image">
<a href="{{ product.url }}">
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
</a>
</div>
<div class="upsell-info">
<p class="upsell-title">
<a href="{{ product.url }}" class="upsell-title-link">{{ product.title }}</a>
</p>
<p class="upsell-price">{{ product.price | money }}</p>
<button class="upsell-add-button" type="button">Add</button>
</div>
</div>
</div>
{% endunless %}
{% endfor %}
</div>
</div>
<div class="upsell-nav-prev" aria-label="Previous slide">‹</div>
<div class="upsell-nav-next" aria-label="Next slide">›</div>
</div>
{% else %}
<div class="cart-upsell-products">
{% for product in settings.cart_upsell_products %}
{% unless cart_product_ids contains product.id %}
<div class="upsell-product">
<div class="upsell-inner" data-variant-id="{{ product.variants.first.id }}">
<div class="upsell-image">
<a href="{{ product.url }}">
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
</a>
</div>
<div class="upsell-info">
<p class="upsell-title">
<a href="{{ product.url }}" class="upsell-title-link">{{ product.title }}</a>
</p>
<p class="upsell-price">{{ product.price | money }}</p>
<button class="upsell-add-button" type="button">Add</button>
</div>
</div>
</div>
{% endunless %}
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% endif %}
</div>
<style>
.cart-upsell-module {
padding: 0 8px;
overflow: visible;
}
.cart-upsell-wrapper {
border-top: 1px solid #eee;
overflow: visible;
position: relative;
}
.cart-upsell-wrapper h3 {
margin: 8px 0 4px;
text-align: center;
}
.cart-upsell-nav-wrapper {
position: relative;
width: 90%;
margin: 0 auto;
overflow: visible;
}
.cart-upsell-swiper {
overflow: hidden;
width: 100%;
}
.cart-upsell-swiper .swiper-wrapper {
display: flex;
}
.cart-upsell-swiper .swiper-slide {
flex-shrink: 0;
box-sizing: border-box;
width: 100%;
}
.upsell-inner {
display: flex;
gap: 1rem;
align-items: center;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1rem;
background: white;
width: 100%;
}
.upsell-image {
width: 85px;
flex: 0 0 85px;
}
.upsell-image img {
width: 100%;
height: auto;
border-radius: 6px;
object-fit: cover;
}
.upsell-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.upsell-title {
font-size: 14px;
font-weight: 600;
margin: 0;
color: black;
}
.upsell-title a {
color: inherit;
text-decoration: none;
}
.upsell-title a:hover {
text-decoration: underline;
}
.upsell-price {
font-size: 1rem;
color: #333;
margin: 0;
}
.upsell-add-button {
background: #f0d4bc;
color: #000;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
}
.upsell-nav-prev,
.upsell-nav-next {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 50;
background: rgba(255, 255, 255, 0.9);
width: 32px;
height: 32px;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 20px;
color: #000;
}
.upsell-nav-prev {
left: -16px;
}
.upsell-nav-next {
right: -16px;
}
</style>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.css" />
<script src="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Initialize the Swiper carousel
let upsellSwiper = new Swiper(".cart-upsell-swiper", {
slidesPerView: 1,
spaceBetween: 0,
navigation: {
nextEl: ".upsell-nav-next",
prevEl: ".upsell-nav-prev"
},
watchOverflow: false
});
// Function to re-initialize Swiper after a product is added to the cart
function reinitializeSwiper() {
// Destroy and recreate Swiper to ensure the DOM is correctly recognized
upsellSwiper.destroy(true, true);
upsellSwiper = new Swiper(".cart-upsell-swiper", {
slidesPerView: 1,
spaceBetween: 0,
navigation: {
nextEl: ".upsell-nav-next",
prevEl: ".upsell-nav-prev"
},
watchOverflow: false
});
}
// Handle the 'Add to Cart' button click event
document.querySelectorAll(".upsell-add-button").forEach(button => {
button.addEventListener("click", async function (e) {
e.preventDefault();
e.stopPropagation();
const parent = this.closest(".upsell-inner");
const variantId = parent.dataset.variantId;
try {
// Add the product to the cart
const response = await fetch("/cart/add.js", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify({ id: variantId, quantity: 1 })
});
const addedProduct = await response.json();
// Fetch updated cart drawer HTML (this will include changes after adding the product)
const cartResponse = await fetch(`${Shopify.routes.cart_url}?section_id=cart-drawer`);
const cartHtml = await cartResponse.text();
const html = new DOMParser().parseFromString(cartHtml, 'text/html');
// Replace the cart drawer with the updated one
const cartDrawer = document.querySelector('cart-drawer');
if (cartDrawer) {
cartDrawer.innerHTML = html.querySelector('cart-drawer').innerHTML;
}
// Update cart count manually
const updatedCart = await fetch('/cart.js').then(res => res.json());
const itemCount = updatedCart.item_count;
const cartIcon = document.querySelector('#cart-icon-bubble');
let cartCountBubble = cartIcon?.querySelector('.cart-count-bubble span');
if (cartCountBubble) {
cartCountBubble.textContent = itemCount;
} else if (cartIcon) {
const bubble = document.createElement('div');
bubble.className = 'cart-count-bubble';
const span = document.createElement('span');
span.setAttribute('aria-hidden', 'true');
span.textContent = itemCount;
bubble.appendChild(span);
cartIcon.appendChild(bubble);
}
// Reinitialize the Swiper carousel after the DOM is updated
reinitializeSwiper();
} catch (error) {
console.error("Cart update failed:", error);
}
});
});
});
</script>
Solved! Go to the solution
This is an accepted solution.
When page is loaded initially, your JS code runs and initializes swiper, etc. Swiper works.
On each cart change theme Javascript completely replaces content of the cart drawer. This means that any elements which existed there are no longer there and any event listeners attached to them are no longer functional.
This is done by replacing innerHTML of the drawer wrapper element. Note, that any <script>...</script> code added this way is not executed.
And on top of that, your code is inside event listener which listens to "DOMContentLoaded" which has already fired and would not fire again.
So you need to make sure your that your JS runs after cart drawer update.
One way to do this is to subscribe to PUB_SUB_EVENTS.cartUpdate which is fired upon cart drawer update and run your swiper init code there.
Another option is to define a custom element and put your html inside this custom element. The beauty of custom elements is that the browser will automatically initialize them with your code as soon as they added to the document, so you do not need to watch for events, etc.
This is an accepted solution.
When page is loaded initially, your JS code runs and initializes swiper, etc. Swiper works.
On each cart change theme Javascript completely replaces content of the cart drawer. This means that any elements which existed there are no longer there and any event listeners attached to them are no longer functional.
This is done by replacing innerHTML of the drawer wrapper element. Note, that any <script>...</script> code added this way is not executed.
And on top of that, your code is inside event listener which listens to "DOMContentLoaded" which has already fired and would not fire again.
So you need to make sure your that your JS runs after cart drawer update.
One way to do this is to subscribe to PUB_SUB_EVENTS.cartUpdate which is fired upon cart drawer update and run your swiper init code there.
Another option is to define a custom element and put your html inside this custom element. The beauty of custom elements is that the browser will automatically initialize them with your code as soon as they added to the document, so you do not need to watch for events, etc.
Hi @TrendBlend
Replace your code with this code, and If my reply is helpful, kindly click like and mark it as an accepted solution.
Thanks!
<!-- Upsell Container -->
<div class="cart-upsell-module">
{% if settings.cart_upsell_products != blank %}
{% assign cart_product_ids = cart.items | map: 'product_id' %}
{% assign upsell_count = 0 %}
{% for product in settings.cart_upsell_products %}
{% unless cart_product_ids contains product.id %}
{% assign upsell_count = upsell_count | plus: 1 %}
{% endunless %}
{% endfor %}
{% if upsell_count > 0 %}
<div class="cart-upsell-wrapper">
<h3>Je kunt dit ook leuk vinden</h3>
{% if upsell_count > 1 %}
<div class="cart-upsell-nav-wrapper">
<div class="swiper cart-upsell-swiper">
<div class="swiper-wrapper">
{% for product in settings.cart_upsell_products %}
{% unless cart_product_ids contains product.id %}
<div class="swiper-slide">
<div class="upsell-inner" data-variant-id="{{ product.variants.first.id }}">
<div class="upsell-image">
<a href="{{ product.url }}">
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
</a>
</div>
<div class="upsell-info">
<p class="upsell-title">
<a href="{{ product.url }}" class="upsell-title-link">{{ product.title }}</a>
</p>
<p class="upsell-price">{{ product.price | money }}</p>
<button class="upsell-add-button" type="button">Add</button>
</div>
</div>
</div>
{% endunless %}
{% endfor %}
</div>
</div>
<div class="upsell-nav-prev" aria-label="Previous slide">‹</div>
<div class="upsell-nav-next" aria-label="Next slide">›</div>
</div>
{% else %}
<div class="cart-upsell-products">
{% for product in settings.cart_upsell_products %}
{% unless cart_product_ids contains product.id %}
<div class="upsell-product">
<div class="upsell-inner" data-variant-id="{{ product.variants.first.id }}">
<div class="upsell-image">
<a href="{{ product.url }}">
<img src="{{ product.featured_image | img_url: 'medium' }}" alt="{{ product.title }}">
</a>
</div>
<div class="upsell-info">
<p class="upsell-title">
<a href="{{ product.url }}" class="upsell-title-link">{{ product.title }}</a>
</p>
<p class="upsell-price">{{ product.price | money }}</p>
<button class="upsell-add-button" type="button">Add</button>
</div>
</div>
</div>
{% endunless %}
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% endif %}
</div>
<style>
.cart-upsell-module {
padding: 0 8px;
overflow: visible;
}
.cart-upsell-wrapper {
border-top: 1px solid #eee;
overflow: visible;
position: relative;
}
.cart-upsell-wrapper h3 {
margin: 8px 0 4px;
text-align: center;
}
.cart-upsell-nav-wrapper {
position: relative;
width: 90%;
margin: 0 auto;
overflow: visible;
}
.cart-upsell-swiper {
overflow: hidden;
width: 100%;
}
.cart-upsell-swiper .swiper-wrapper {
display: flex;
}
.cart-upsell-swiper .swiper-slide {
flex-shrink: 0;
box-sizing: border-box;
width: 100%;
}
.upsell-inner {
display: flex;
gap: 1rem;
align-items: center;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1rem;
background: white;
width: 100%;
}
.upsell-image {
width: 85px;
flex: 0 0 85px;
}
.upsell-image img {
width: 100%;
height: auto;
border-radius: 6px;
object-fit: cover;
}
.upsell-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.upsell-title {
font-size: 14px;
font-weight: 600;
margin: 0;
color: black;
}
.upsell-title a {
color: inherit;
text-decoration: none;
}
.upsell-title a:hover {
text-decoration: underline;
}
.upsell-price {
font-size: 1rem;
color: #333;
margin: 0;
}
.upsell-add-button {
background: #f0d4bc;
color: #000;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
}
.upsell-nav-prev,
.upsell-nav-next {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 50;
background: rgba(255, 255, 255, 0.9);
width: 32px;
height: 32px;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 20px;
color: #000;
}
.upsell-nav-prev {
left: -16px;
}
.upsell-nav-next {
right: -16px;
}
</style>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.css" />
<script src="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
let upsellSwiper = null;
// Function to initialize or reinitialize Swiper
function initializeSwiper() {
// Destroy existing Swiper instance if it exists
if (upsellSwiper) {
upsellSwiper.destroy(true, true);
}
// Initialize Swiper only if the swiper container exists
const swiperContainer = document.querySelector(".cart-upsell-swiper");
if (swiperContainer) {
upsellSwiper = new Swiper(".cart-upsell-swiper", {
slidesPerView: 1,
spaceBetween: 0,
navigation: {
nextEl: ".upsell-nav-next",
prevEl: ".upsell-nav-prev",
},
watchOverflow: false,
});
}
}
// Function to initialize event listeners for "Add to Cart" buttons
function initializeAddToCart() {
// Use event delegation to handle clicks on dynamically added buttons
document.body.addEventListener("click", async function (e) {
const button = e.target.closest(".upsell-add-button");
if (!button) return;
e.preventDefault();
e.stopPropagation();
const parent = button.closest(".upsell-inner");
const variantId = parent.dataset.variantId;
try {
// Add the product to the cart
const response = await fetch("/cart/add.js", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ id: variantId, quantity: 1 }),
});
const addedProduct = await response.json();
// Fetch updated cart drawer HTML
const cartResponse = await fetch(`${Shopify.routes.cart_url}?section_id=cart-drawer`);
const cartHtml = await cartResponse.text();
const html = new DOMParser().parseFromString(cartHtml, "text/html");
// Replace the cart drawer with the updated one
const cartDrawer = document.querySelector("cart-drawer");
if (cartDrawer) {
cartDrawer.innerHTML = html.querySelector("cart-drawer").innerHTML;
}
// Update cart count
const updatedCart = await fetch("/cart.js").then((res) => res.json());
const itemCount = updatedCart.item_count;
const cartIcon = document.querySelector("#cart-icon-bubble");
let cartCountBubble = cartIcon?.querySelector(".cart-count-bubble span");
if (cartCountBubble) {
cartCountBubble.textContent = itemCount;
} else if (cartIcon) {
const bubble = document.createElement("div");
bubble.className = "cart-count-bubble";
const span = document.createElement("span");
span.setAttribute("aria-hidden", "true");
span.textContent = itemCount;
bubble.appendChild(span);
cartIcon.appendChild(bubble);
}
// Reinitialize Swiper after DOM update
initializeSwiper();
} catch (error) {
console.error("Cart update failed:", error);
}
});
}
// Initialize Swiper and event listeners on page load
initializeSwiper();
initializeAddToCart();
// Listen for Shopify cart updates (e.g., quantity change, remove item)
document.addEventListener("cart:updated", function () {
initializeSwiper();
});
// Fallback: Use MutationObserver to detect cart drawer DOM changes
const cartDrawer = document.querySelector("cart-drawer");
if (cartDrawer) {
const observer = new MutationObserver(() => {
initializeSwiper();
});
observer.observe(cartDrawer, { childList: true, subtree: true });
}
});
</script>
thank you so much!
June brought summer energy to our community. Members jumped in with solutions, clicked ...
By JasonH Jun 5, 2025Learn how to build powerful custom workflows in Shopify Flow with expert guidance from ...
By Jacqui May 7, 2025Did You Know? May is named after Maia, the Roman goddess of growth and flourishing! ...
By JasonH May 2, 2025