Skip to content
/*
* Find the bundle quantity based on the text
* contained in the selected/clicked bundle option.
*/
function getBundleQuantity(element) {
if (!element) return null;
let current = element;
for (let i = 0; i < 6 && current; i++) {
const text = current.textContent || '';
const match =
text.match(/Buy\s*(1|3|5|10)\b/i) ||
text.match(/\b(1|3|5|10)[-\s]?Pack\b/i);
if (match) {
return parseInt(match[1], 10);
}
current = current.parentElement;
}
return null;
}
function updateMainProductPrice(quantity) {
const data = bundleData[quantity];
if (!data) return;
/*
* MAIN PRICE
*
* Covers the most common Shopify theme
* price selectors.
*/
const selectors = [
'.product-info__price .price-item--regular',
'.product__info-container .price-item--regular',
'.product-info .price-item--regular',
'.product__price',
'[data-product-price]',
'.price__regular .price-item--regular'
];
let priceUpdated = false;
selectors.forEach(selector => {
document.querySelectorAll(selector).forEach(el => {
/*
* Don't overwrite prices inside
* bundle/app widgets or recommendations.
*/
const text = el.closest('form')?.textContent || '';
if (
text.includes('Buy 3') &&
text.includes('Buy 5') &&
text.includes('Buy 10')
) {
return;
}
if (!priceUpdated) {
el.textContent = data.price;
priceUpdated = true;
}
});
});
}
function updateAddToCartButton(quantity) {
const data = bundleData[quantity];
if (!data) return;
const buttons = document.querySelectorAll(
'product-form button[type="submit"], ' +
'form[action*="/cart/add"] button[type="submit"], ' +
'button[name="add"]'
);
buttons.forEach(button => {
/*
* Make sure we're touching the main
* product Add to Cart button.
*/
const text = button.textContent.trim().toLowerCase();
if (!text.includes('add to cart')) return;
/*
* Find the element containing the visible
* Add to Cart wording.
*/
const spans = button.querySelectorAll('span');
let target = null;
spans.forEach(span => {
if (span.textContent.toLowerCase().includes('add to cart')) {
target = span;
}
});
target = target || button;
target.textContent = 'ADD TO CART • ' + data.price;
});
}
function updatePaymentTerms(quantity) {
/*
* Shopify calculates Shop Pay installments
* from the $169 variant, so hide it for bundles
* rather than displaying incorrect financing.
*/
const terms = document.querySelectorAll(
'shopify-payment-terms, .shopify-payment-terms'
);
terms.forEach(el => {
el.style.display = quantity === 1 ? '' : 'none';
});
}
function updateBundleDisplay(quantity) {
if (!bundleData[quantity]) return;
updateMainProductPrice(quantity);
updateAddToCartButton(quantity);
updatePaymentTerms(quantity);
}
/*
* Detect interaction with any bundle option.
*
* We use click + change because some bundle apps
* use custom cards instead of standard radio inputs.
*/
document.addEventListener('click', function(event) {
const quantity = getBundleQuantity(event.target);
if (!quantity) return;
/*
* Small delay lets the bundle app finish changing
* its own selected state first.
*/
setTimeout(function () {
updateBundleDisplay(quantity);
}, 100);
});
document.addEventListener('change', function(event) {
const quantity = getBundleQuantity(event.target);
if (!quantity) return;
setTimeout(function () {
updateBundleDisplay(quantity);
}, 100);
});
/*
* Initial state
*/
updateBundleDisplay(1);
});