Example : Custom Images Logic Based On Product Attributes
This script adds a hover image effect to products in “Discover” collections — showing an alternate image when hovering, and updating images dynamically when users click or hover on color swatches.
JS Code:
var __fast_options = __fast_options || {};
__fast_options.with_product_attributes = true;
/* Utility: get all product cards */
function getProductElements(element) {
    return [...element.querySelectorAll(`.fs-results-product-card`)];
}
/* Utility: extract the secondary image (prefers ones containing “flat”) */
function getSecondProductImage(attributes) {
    for (const [key, value] of attributes) {
        if (key === "Product images" && Array.isArray(value) && value.length > 0) {
            const flatImage = value.find(url => url.toLowerCase().includes("flat"));
            if (flatImage) return flatImage;
            if (value.length >= 3) return value[2];
            return null;
        }
    }
    return null;
}
/* Main logic hook */
function hooks() {
    SerpOptions.registerHook('serp-product-grid', ({ products, element }) => {
        for (const productElement of getProductElements(element)) {
            const productID = productElement.dataset.productId;
            const data = products[productID];
            const attributes = data?.attributes;
            // Only for “Discover” collections
            if (window.location.pathname.toLowerCase().includes('/collections/discover') && attributes) {
                const imageContainer = productElement.querySelector('.image-container');
                if (!imageContainer) continue;
                const second = getSecondProductImage(attributes);
                const originalImg = imageContainer.querySelector('img:not(.cloned)');
                const imageWrapper = productElement.querySelector('.image-container .wrapper');
                if (imageWrapper && second && originalImg && !imageContainer.classList.contains("cstm")) {
                    // Clone images
                    const firstImageClone = originalImg.cloneNode(true);
                    const secondImageClone = originalImg.cloneNode(true);
                    firstImageClone.classList.add("cloned", "first");
                    firstImageClone.src = second;
                    secondImageClone.classList.add("cloned", "second");
                    // Hide original
                    originalImg.style.display = "none";
                    // Append clones
                    imageWrapper.appendChild(firstImageClone);
                    imageWrapper.appendChild(secondImageClone);
                    // Hover swap
                    imageContainer.addEventListener('mouseleave', () => {
                        firstImageClone.style.display = "flex";
                        secondImageClone.style.display = "none";
                    });
                    imageContainer.addEventListener('mouseenter', () => {
                        firstImageClone.style.display = "none";
                        secondImageClone.style.display = "flex";
                    });
                    // Mark processed
                    imageContainer.classList.add('cstm');
                }
                // Update images when color swatches are hovered or clicked
                productElement.querySelectorAll(".color-swatch:not(.cstm-event)").forEach((swatch) => {
                    swatch.classList.add("cstm-event");
                    const colorContainer = swatch.closest(".color-swatch-container");
                    const colorName = colorContainer.querySelector(".color-swatch-color-name")?.innerHTML;
                    if (colorName) {
                        const swatchProductData = data.alternativeProducts.find(
                            altProduct => altProduct["color"].trim().toLowerCase() == colorName.trim().toLowerCase()
                        );
                        if (swatchProductData && originalImg) {
                            swatch.addEventListener('click', () => {
                                const secondImage = getSecondProductImage(swatchProductData.attributes);
                                const firstImage = originalImg.src;
                                if (secondImage && firstImage) {
                                    imageContainer.querySelector('img.cloned.first').src = secondImage;
                                    imageContainer.querySelector('img.cloned.second').src = firstImage;
                                }
                            });
                            colorContainer.addEventListener('mouseover', () => {
                                swatch.click();
                            });
                        }
                    }
                });
            }
        }
    });
}
/* Execute after Fast Simon SERP is ready */
if (window.SerpOptions) {
    hooks();
} else {
    window.addEventListener('fast-serp-ready', function () {
        hooks();
    });
}
Note
- The example handles alternative products but possible on "regular" color variants with data adjustments