quick add button adds duplicate card each time it is clicked

Topic summary

A developer is experiencing an issue where clicking a “quick add” button creates duplicate product cards in a cart drawer instead of updating the quantity of existing items.

Technical Context:

  • The code uses jQuery to load cart items via Shopify’s /cart.js endpoint
  • Logic exists to check for existing product cards using data-product-id attributes and update quantities
  • If no card exists, it should create a new one; if it does exist, it should only update the quantity display

Current Status:

  • The developer shared their complete JavaScript implementation, including functions for loading cart items, generating product cards, and handling quantity updates
  • A helper (@websensepro) requested the store URL and password to investigate
  • Store credentials provided: the issue occurs with “quick add” buttons on product cards in the “plants of the week” section

Next Steps:
Awaiting troubleshooting assistance to identify why the duplicate detection logic isn’t working as intended.

Summarized with AI on November 2. AI used: claude-sonnet-4-5-20250929.

$(document).ready(function () {
// Load cart items on page load
loadCartItems();

function loadCartItems() {
$.getJSON(“/cart.js”, function (cartData) {
$(“#subtotal-amount”).text(cartData.items_subtotal_price);

// Iterate over each item in the cart (cartData.items)
cartData.items.forEach((item) => {
// Check if a product card for this product already exists in the UI.
const existingCard = $(
.drawer-product-card[data-product-id='${item.product_id}']
);

// If the card exists, update the quantity.
if (existingCard.length > 0) {
const quantityElement = existingCard.find(“.quantity-number”);
quantityElement.text(item.quantity); // Update the quantity directly
} else {
// If the product card does not exist, create one
const productCardHTML = generateProductCardHTML(item);
$(“.cart-count”).after(productCardHTML); // Add the generated product card HTML
}
});
});
}

// Toast notification
function toastErrorMessage(customMessage) {
var x = $(“#snackbar”).text(customMessage);
x.addClass(“show”);
setTimeout(function () {
x.removeClass(“show”);
}, 3000);
}

function openDrawer() {
$(“.notes-container”).addClass(“active”);
$(“.temporary-drawer”).addClass(“animate”);
$(“.drawer-overlay”).addClass(“animate”);
}

function closeDrawer() {
$(“.notes-container”).removeClass(“active”);
$(“.temporary-drawer”).removeClass(“animate”);
$(“.drawer-overlay”).removeClass(“animate”);
}

$(document).on(“click”, “#favourites-icon”, function () {
openDrawer();
loadCartItems();
});

// Reusable function to generate product card HTML
function generateProductCardHTML(item) {
return `

${item.product_title}
${item.product_title}
${item.product_description}
${currencySymbol}${item.price}
${item.quantity}
`; }

// Event listener for the quick add button
$(document).on(“click”, “.quick-add-icon”, function () {
const dataProductId = $(this).attr(“data-product-id”);

if (!dataProductId) {
toastErrorMessage(“This Product is Not Found.”);
return;
}

const existingCard = $(
.drawer-product-card[data-product-id='${dataProductId}']
);

// Variable to track if the toast has already been shown for quantity update
let toastShown = false;

if (existingCard.length > 0) {
// Product exists, update quantity in the existing card
const quantityElement = existingCard.find(“.quantity-number”);
const currentQuantity = parseInt(quantityElement.text(), 10) || 0;
const newQuantity = currentQuantity + 1;

// Update the quantity in the existing card and in the cart
quantityElement.text(newQuantity);

// Call updateCartQuantity function and handle error
updateCartQuantity(existingCard.attr(“data-line-item-id”), newQuantity)
.done(function () {
// Success, no error needed
})
.fail(function (jqXHR, textStatus, errorThrown) {
const errorMessage = Error: ${textStatus} - ${errorThrown};
toastErrorMessage(errorMessage); // Show the detailed error message in the toast
});

// Show toast only once during this session
if (!toastShown) {
toastErrorMessage(“Item quantity updated in cart.”);
toastShown = true; // Mark the toast as shown
}
} else {
// Product does not exist, add to cart
addProductToCart(dataProductId);
}
});

// Add product to cart

function addProductToCart(dataProductId) {
$.ajax({
type: “POST”,
url: “/cart/add.js”,
dataType: “json”,
data: { quantity: 1, id: dataProductId },
success: function (data) {
// Find the existing product card
const existingCard = $(
.drawer-product-card[data-product-id='${dataProductId}']
);

if (existingCard.length > 0) {
// If the card exists, update the quantity
const quantityElement = existingCard.find(“.quantity-number”);
const currentQuantity = parseInt(quantityElement.text(), 10) || 0;
quantityElement.text(currentQuantity + 1); // Increment the quantity by 1
} else {
// If the card doesn’t exist, create a new card
const productCardHTML = generateProductCardHTML(data);
$(“.cart-count”).after(productCardHTML); // Insert the new product card after cart count
}

// Update cart count and show success message
updateCartCount();
toastErrorMessage(“Item Added To Cart”);
openDrawer();
},
error: function (error) {
console.error(“Error adding to cart:”, error);
if (error.status === 404) {
// If the item is out of stock, show an error message
toastErrorMessage(“Item Out Of Stock.”);
} else {
// General error message
toastErrorMessage(“Failed to add item to cart.”);
}
loadCartItems(); // Re-load cart items to sync the UI
openDrawer(); // Open the drawer for visibility
},
});
}

// Update cart quantity
function updateCartQuantity(productId, quantity) {
$.ajax({
type: “POST”,
url: “/cart/change.js”,
dataType: “json”,
data: {
id: productId,
quantity: quantity,
},
beforeSend: function () {
console.log(
Updating quantity for product ${productId} to ${quantity}
);
},
success: function () {
updateCartCount();
},
error: function (error) {
logError(“Update Quantity”, error);
console.log(error);
toastErrorMessage(“Failed to update cart quantity.”);
},
});
}

// Update cart count and subtotal
function updateCartCount() {
$.getJSON(“/cart.js”, function (cartData) {
console.log(“Cart data fetched successfully:”, cartData);
$(“#subtotal-amount”).text(cartData.items_subtotal_price);

const totalItemsText =
cartData.item_count === 0
? “Your Cart Is Empty”
: ${cartData.item_count} ${ cartData.item_count === 1 ? "Product" : "Products" };

$(“.temporary-drawer .cart-count .total-cart-items”).text(totalItemsText);
}).fail(function (jqXHR, textStatus, errorThrown) {
logError(“Fetch Cart Data”, ${textStatus} - ${errorThrown});
$(“.temporary-drawer .cart-count .total-cart-items”).text(
“Error updating cart.”
);
});
}

// Generic error logging
function logError(context, error) {
console.error(${context} Error:, error);
}

// Other event listeners remain unchanged
$(document).on(“keydown”, function (e) {
if (e.keyCode === 27) {
closeDrawer();
}
});

$(document).on(“click”, “#checkout-close-icon, .drawer-overlay”, function () {
closeDrawer();
});

$(document).on(“click”, “#clear-cart”, function (e) {
e.preventDefault();
clearCart();
});

$(document).on(“click”, “.delete-button”, function () {
const $productCard = $(this).closest(“.drawer-product-card”);
const lineItemId = $productCard.data(“line-item-id”);

$productCard.addClass(“swipe-out”);

setTimeout(() => {
$productCard.remove();
updateCartCount();
}, 300);

$.ajax({
type: “POST”,
url: “/cart/update.js”,
data: { updates: { [lineItemId]: 0 } },
dataType: “json”,
success: function () {
updateCartCount();
},
error: function (error) {
console.error(“Error removing item:”, error);
},
});
});

function clearCart() {
$.ajax({
type: “POST”,
url: “/cart/clear.js”,
dataType: “json”,
success: function () {
$(“.drawer-product-card”).remove();
$(“.temporary-drawer .cart-count .total-cart-items”).text(
“Your Cart Is Empty”
);
$(“.temporary-drawer .cart-total .total-price”).text(“0”);
closeDrawer();
},
error: function (error) {
console.error(“Error clearing cart:”, error);
},
});
}

//handle for the plus button
$(document).on(“click”, “.plus-button”, function () {
const productCard = $(this).closest(“.drawer-product-card”);
const quantityElement = productCard.find(“.quantity-number”);
const currentQuantity = parseInt(quantityElement.text(), 10) || 0;

const newQuantity = currentQuantity + 1; // Increase the quantity by 1

// Get the line item ID
const lineItemId = productCard.attr(“data-line-item-id”);

// Temporarily show the updated quantity in the UI (but we’ll revert it if the cart update fails)
const previousQuantity = currentQuantity; // Save the previous quantity

// Update the UI with the new quantity temporarily
quantityElement.text(newQuantity);

// Use your existing function to update the cart quantity
updateCartQuantity(lineItemId, newQuantity)
.done(function () {
// Successfully updated the cart, refresh cart count and subtotal
updateCartCount();
})
.fail(function () {
// Revert the UI quantity if the cart update failed (e.g., out of stock)
quantityElement.text(previousQuantity); // Revert to the original quantity
// Show toast error message
toastErrorMessage(“Failed to update cart quantity.”);
});
});

$(document).on(“click”, “.minus-button”, function () {
const productCard = $(this).closest(“.drawer-product-card”);
const quantityElement = productCard.find(“.quantity-number”);
let currentQuantity = parseInt(quantityElement.text(), 10) || 0;

if (currentQuantity > 1) {
// Decrease the quantity by 1
const newQuantity = currentQuantity - 1;

// Update the quantity in the product card
quantityElement.text(newQuantity);

// Get the line item ID
const lineItemId = productCard.attr(“data-line-item-id”);

// Update the cart with the new quantity
updateCartQuantity(lineItemId, newQuantity)
.done(function () {
updateCartCount(); // Optionally, refresh the cart count and subtotal after updating the cart
})
.fail(function () {
// Show toast error message if there’s a failure
toastErrorMessage(“Failed to update cart quantity.”);
});
} else {
// If the quantity is 1, remove the item from the cart and UI
removeProductFromCart(productCard);
}
});

// Remove product from cart and UI
function removeProductFromCart(productCard) {
const lineItemId = productCard.attr(“data-line-item-id”);

// Remove the item from the cart
$.ajax({
type: “POST”,
url: “/cart/update.js”,
data: {
updates: { [lineItemId]: 0 }, // Set the quantity to 0 to remove the item
},
dataType: “json”,
success: function () {
// Remove the product card from the UI
productCard.addClass(“swipe-out”); // Optionally, add a swipe-out effect for animation

// After animation, remove the product card from the DOM
setTimeout(() => {
productCard.remove();
updateCartCount(); // Refresh the cart count and subtotal
}, 300); // Match the duration of the swipe-out effect
},
error: function (error) {
console.error(“Error removing item:”, error);
toastErrorMessage(“Failed to remove item from cart.”);
},
});
}
});

Hi @abhicadisoft , kindly provide your store URL please and if it is password protected, please share the password as well. Thanks

https://qdzo1m8m0d22pi45-72122302722.shopifypreview.com

password → sautwa

scroll down where you can see plants of the weeks and there will be a quick add button on the each product card