Skip to content

FX-Klaviyo/Klaviyo_Shopify_Cart_tracking_Enhancement

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 

Repository files navigation

Klaviyo_Shopify_Cart_tracking_Enhancement

Overview

This script is designed for Shopify to track cart-related events using the Klaviyo tracking API. It detects when items are added, removed, or their quantity is adjusted in the cart and sends event data to Klaviyo.


Summary

  • Fetches Cart Data: Retrieves the latest cart state from Shopify.
  • Tracks Changes: Detects when items are:
    • Added
    • Removed
    • Quantity Reduced
    • Cart Emptied
  • Includes Product Details: Captures key details such as:
    • Product ID, Name, Price, Quantity
    • Image URL, Product URL, Collections
    • Currency and Variant Information
  • Intercepts Cart Updates: Listens for changes by overriding:
    • fetch requests
    • XMLHttpRequest (AJAX requests)
  • Sends Klaviyo Events: Logs each cart update with event-specific details:
    • Added to Cart
    • Removed from Cart
    • Quantity Reduced
    • Cart Emptied

Purpose

  • Provides real-time cart tracking for analytics.
  • Enables personalized marketing automation using Klaviyo.
  • Ensures accurate event logging for customer behavior insights.

Instruction

  1. In Shopify, navigate to Online Store > Themes.
  2. Find your theme and click Customize.
  3. Click the three dots at the top and select Edit code.
  4. Open the theme.liquid file.
  5. Paste the provided snippet after all other code, before the closing </body> tag.

The Script

<script>
document.addEventListener('DOMContentLoaded', () => {
    const klaviyo = window.klaviyo || [];
    let lastCartState = null;

    // 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;
        }
    };

    // Retrieve collections for a given product
    const getCollectionsForProduct = (productId) => window.productCollections?.[productId] || [];

    // Format a single cart item to include ImageURL and Collections
    const formatCartItem = (item, cartCurrency) => ({
        ProductID: item.product_id || item.id,
        Name: item.product_title,
        Price: item.final_line_price / 100,
        Quantity: item.quantity,
        Currency: cartCurrency,
        VariantID: item.variant_id || null,
        ImageURL: item.image?.src || '', // Restore Image URL
        Collections: getCollectionsForProduct(item.product_id) // Restore Collections
    });

    // Convert full cart data into an array of formatted items
    const formatCartItems = (cart) => cart.items.map(item => formatCartItem(item, cart.currency));

    // Track cart-related events
    const trackEvent = (eventName, data) => {
        console.log(`${eventName} Event Tracked:`, data);
        klaviyo.push(['track', eventName, data]);
    };

    // Compare previous and current cart states to detect changes
    const compareCartStates = (prevCart, newCart) => {
        const prevCartMap = new Map(prevCart.items.map(item => [item.id, item]));
        const newCartMap = new Map(newCart.items.map(item => [item.id, item]));

        let addedItems = [];
        let removedItems = [];
        let quantityReducedItems = [];

        // Detect newly added or increased quantity items
        newCart.items.forEach(newItem => {
            const prevItem = prevCartMap.get(newItem.id);
            if (!prevItem || newItem.quantity > prevItem.quantity) {
                addedItems.push(newItem);
            }
        });

        // Detect removed or quantity-reduced items
        prevCart.items.forEach(prevItem => {
            const newItem = newCartMap.get(prevItem.id);
            if (!newItem) {
                removedItems.push(prevItem);
            } else if (newItem.quantity < prevItem.quantity) {
                quantityReducedItems.push({
                    ...prevItem,
                    quantity: prevItem.quantity - newItem.quantity,
                    unit_price: prevItem.final_line_price / prevItem.quantity / 100
                });
            }
        });

        return { addedItems, removedItems, quantityReducedItems };
    };

    // Record and track cart changes
    const recordCartChange = async () => {
        const newCart = await fetchCartData();
        if (!newCart) return;

        const cartValue = newCart.total_price / 100;
        const pageURL = window.location.href;
        const formattedItems = formatCartItems(newCart);

        // Track "Cart Emptied" event
        if (newCart.items.length === 0) {
            trackEvent('Cart Emptied', { total_price: 0, $value: 0, Currency: newCart.currency, PageURL: pageURL, items: [] });
        }

        if (lastCartState) {
            const { addedItems, removedItems, quantityReducedItems } = compareCartStates(lastCartState, newCart);

            addedItems.forEach(item => trackEvent('Added to Cart', {
                ...formatCartItem(item, newCart.currency), // Restore ImageURL & Collections
                ProductURL: item.url,
                Brand: item.vendor,
                Price: item.final_line_price / item.quantity / 100,
                CompareAtPrice: item.compare_at_price / 100,
                PresentmentCurrency: newCart.currency,
                VariantTitle: item.variant_title || '',
                items: formattedItems,
                $value: cartValue,
                RecordedEventURL: pageURL
            }));

            removedItems.forEach(item => trackEvent('Removed from Cart', {
                ...formatCartItem(item, newCart.currency), // Restore ImageURL & Collections
                ProductURL: item.url,
                Price: item.final_line_price / item.quantity / 100,
                Quantity: item.quantity,
                items: formattedItems,
                $value: cartValue,
                RecordedEventURL: pageURL
            }));

            quantityReducedItems.forEach(item => trackEvent('Quantity Reduced', {
                ...formatCartItem(item, newCart.currency), // Restore ImageURL & Collections
                ProductURL: item.url,
                Price: item.unit_price,
                items: formattedItems,
                $value: cartValue,
                RecordedEventURL: pageURL
            }));
        }

        lastCartState = newCart;
    };

    // Intercept cart API requests to detect changes
    const interceptCartChanges = () => {
        const originalFetch = window.fetch.bind(window);
        window.fetch = async (...args) => {
            const response = await originalFetch(...args);
            if (response.url.includes(`${window.location.origin}/cart/`)) await recordCartChange();
            return response;
        };

        const originalXHR = window.XMLHttpRequest.prototype.open;
        window.XMLHttpRequest.prototype.open = function (method, url) {
            if (url.includes('/cart/')) {
                this.addEventListener('load', async () => await recordCartChange());
            }
            return originalXHR.apply(this, arguments);
        };
    };

    // Initialize cart tracking
    (async () => {
        lastCartState = await fetchCartData();
        interceptCartChanges();
    })();
});


</script>

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published