Releases: FX-Klaviyo/Klaviyo_Shopify_Cart_tracking_Enhancement
Releases · FX-Klaviyo/Klaviyo_Shopify_Cart_tracking_Enhancement
first version
`<script>
document.addEventListener('DOMContentLoaded', () => {
const klaviyo = window.klaviyo || []; // Initialize Klaviyo tracking array
let lastCartState = null; // Store the last known cart state for comparison
// Function to fetch the latest cart data from Shopify
const fetchCartData = async () => {
try {
const response = await fetch(`${window.location.origin}/cart.js`);
if (!response.ok) throw new Error('Failed to fetch cart');
return await response.json();
} catch (error) {
console.error('Error fetching cart data:', error);
return null;
}
};
// Function to retrieve product collections based on product ID
const getCollectionsForProduct = (productId) => {
return window.productCollections?.[productId] || [];
};
// Function to record cart changes and send tracking events to Klaviyo
const recordCartChange = async () => {
const newCart = await fetchCartData(); // Fetch updated cart data
if (!newCart) return;
const cartValue = newCart.total_price / 100; // Convert price from cents to dollars
const pageURL = window.location.href; // Capture the URL where the event occurs
// Convert cart items into a structured array with collections data
const items = newCart.items.map(item => ({
ProductID: item.product_id || item.id,
Name: item.product_title,
Price: item.final_line_price / 100,
Quantity: item.quantity,
Currency: newCart.currency,
VariantID: item.variant_id || null,
Collections: getCollectionsForProduct(item.product_id) // Include product collections
}));
console.log("Cart Items with Collections:", items);
// Check if the cart is empty and send a 'Cart Emptied' event
if (newCart.items.length === 0) {
console.log('Cart Emptied');
klaviyo.push(['track', 'Cart Emptied', {
total_price: 0,
$value: 0,
total_discount: 0,
original_total_price: 0,
items: [],
Currency: newCart.currency,
PageURL: pageURL
}]);
}
if (lastCartState) {
let addedItems = [];
let removedItems = [];
let quantityReducedItems = [];
// Create maps for easy comparison of previous and new cart states
let prevCartMap = new Map(lastCartState.items.map(item => [item.id, item]));
let newCartMap = new Map(newCart.items.map(item => [item.id, item]));
// Identify added items
newCart.items.forEach(newItem => {
let prevItem = prevCartMap.get(newItem.id);
if (!prevItem || newItem.quantity > prevItem.quantity) {
addedItems.push(newItem);
}
});
// Identify removed or quantity-reduced items
lastCartState.items.forEach(prevItem => {
let newItem = newCartMap.get(prevItem.id);
if (!newItem) {
removedItems.push(prevItem); // Item removed completely
} else if (newItem.quantity < prevItem.quantity) {
quantityReducedItems.push({
...prevItem,
quantity: prevItem.quantity - newItem.quantity,
unit_price: prevItem.final_line_price / prevItem.quantity / 100
});
}
});
// Track 'Added to Cart' events
addedItems.forEach(item => {
console.log('Item Added:', item);
klaviyo.push(['track', 'Added to Cart', {
ProductID: item.product_id || item.id,
Name: item.product_title,
ImageURL: item.image.src,
ProductURL: item.url,
Brand: item.vendor,
Price: item.final_line_price / item.quantity / 100,
CompareAtPrice: item.compare_at_price / 100,
PresentmentCurrency: newCart.currency,
VariantID: item.variant_id || null,
VariantTitle: item.variant_title || '',
Collections: getCollectionsForProduct(item.product_id), // Include collections
items: items,
$value: cartValue,
RecordedEventURL: pageURL
}]);
});
// Track 'Removed from Cart' events
removedItems.forEach(item => {
console.log('Item Removed Completely:', item);
klaviyo.push(['track', 'Removed from Cart', {
ProductID: item.product_id || item.id,
Name: item.product_title,
ImageURL: item.image.src,
ProductURL: item.url,
Price: item.final_line_price / item.quantity / 100,
Quantity: item.quantity,
Currency: newCart.currency,
VariantID: item.variant_id || null,
Collections: getCollectionsForProduct(item.product_id), // Include collections
items: items,
$value: cartValue,
RecordedEventURL: pageURL
}]);
});
// Track 'Quantity Reduced' events
quantityReducedItems.forEach(item => {
console.log('Item Quantity Reduced:', item);
klaviyo.push(['track', 'Quantity Reduced', {
ProductID: item.product_id || item.id,
Name: item.product_title,
ImageURL: item.image.src,
ProductURL: item.url,
Price: item.unit_price,
Quantity: item.quantity,
Currency: newCart.currency,
VariantID: item.variant_id || null,
Collections: getCollectionsForProduct(item.product_id), // Include collections
items: items,
$value: cartValue,
RecordedEventURL: pageURL
}]);
});
}
lastCartState = newCart; // Update the last known cart state
};
// Function to intercept cart changes by overriding fetch and XMLHttpRequest
const interceptCartChanges = () => {
const originalFetch = window.fetch.bind(window);
window.fetch = (...args) => {
return originalFetch(...args).then(async response => {
if (response.url.includes(`${window.location.origin}/cart/`)) {
await recordCartChange(); // Trigger cart tracking when cart is updated
}
return response;
});
};
// Override XMLHttpRequest to track cart updates in AJAX requests
const originalXHR = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function(method, url) {
if (url.includes('/cart/')) {
this.addEventListener('load', async function() {
await recordCartChange();
});
}
return originalXHR.apply(this, arguments);
};
};
// Initialize tracking: Fetch initial cart state and start intercepting changes
(async () => {
lastCartState = await fetchCartData();
interceptCartChanges();
})();
});
</script>
`