如何为Shopify集合商品网格注入心愿单应用代码并获取产品信息?
解决Shopify集合页商品网格注入心愿单图标的通用方案
一、先搞定产品核心数据的可靠获取
你之前用JS拿不到产品ID、变体ID的问题,核心是别依赖主题的HTML结构,换两种更稳的方式:
1. 用Liquid注入隐藏数据(最推荐,兼容性拉满)
做Shopify应用的话,直接用Theme App Extensions创建一个Liquid片段,让商家把它加到集合页的产品循环里(或者通过应用自动注入到主题的产品项位置)。片段代码示例:
{% comment %} 心愿单应用产品数据注入片段 {% endcomment %} <div class="wishlist-product-data" style="display: none;" data-product-id="{{ product.id }}" data-variant-id="{{ product.selected_or_first_available_variant.id }}" data-product-handle="{{ product.handle }}"> </div>
这个片段会在每个商品项里生成一个隐藏容器,JS直接找这个容器就能拿到精准的产品数据,完全不受主题结构影响。
2. 从现有元素解析数据(兼容无法加Liquid的场景)
如果没法让商家添加Liquid片段,就通过JS扒页面里的现有元素:
- 从商品链接提句柄:商品链接一般是
/products/[handle],正则直接捞:const productLink = item.querySelector('a[href^="/products/"]'); const productHandle = productLink.href.match(/\/products\/([^\/?]+)/)[1]; - 从主题自带的属性拿ID:很多主题会给商品项加
data-product-id,直接取就行:const productId = item.dataset.productId; - 变体ID:看添加购物车按钮上的
data-variant-id属性,有的话直接拿:const variantId = item.querySelector('[data-variant-id]').dataset.variantId;
二、通用JS注入心愿单图标
结合上面的数据获取方式,写个通用的JS脚本,自动遍历所有商品项插图标:
document.addEventListener('DOMContentLoaded', function() { // 匹配常见主题的商品项类名,多写几个提高兼容性 const productItems = document.querySelectorAll('.product-item, .grid-item, .product-card, .col-product, .product-grid-item'); productItems.forEach(item => { // 避免重复插图标 if (item.querySelector('.wishlist-icon')) return; let productId, variantId, productHandle; // 优先用注入的隐藏容器拿数据 const dataContainer = item.querySelector('.wishlist-product-data'); if (dataContainer) { productId = dataContainer.dataset.productId; variantId = dataContainer.dataset.variantId; productHandle = dataContainer.dataset.productHandle; } else { // 降级解析现有元素 const productLink = item.querySelector('a[href^="/products/"]'); if (productLink) productHandle = productLink.href.match(/\/products\/([^\/?]+)/)[1]; productId = item.dataset.productId || item.querySelector('[data-product-id]')?.dataset.productId; variantId = item.dataset.variantId || item.querySelector('[data-variant-id]')?.dataset.variantId; } // 没核心数据的项直接跳过 if (!productId && !productHandle) return; // 创建心愿单图标按钮 const wishlistIcon = document.createElement('button'); wishlistIcon.className = 'wishlist-icon'; wishlistIcon.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>'; wishlistIcon.setAttribute('aria-label', '添加到心愿单'); // 绑定点击事件,替换成你的心愿单逻辑 wishlistIcon.addEventListener('click', function(e) { e.preventDefault(); // 这里调用你的心愿单API,传入拿到的产品数据 console.log('添加到心愿单:', { productId, variantId, productHandle }); }); // 把图标插到合适位置:优先商品图片容器(绝对定位),不行就插商品项开头 const imageContainer = item.querySelector('.product-image, .card-image, .product__img'); if (imageContainer) { imageContainer.style.position = 'relative'; Object.assign(wishlistIcon.style, { position: 'absolute', top: '10px', right: '10px', background: '#fff', border: 'none', borderRadius: '50%', padding: '8px', cursor: 'pointer', zIndex: 10 }); imageContainer.appendChild(wishlistIcon); } else { item.prepend(wishlistIcon); } }); });
三、兼容动态加载场景
如果主题用了无限滚动或分页加载,得监听DOM变化,新商品加载后自动插图标:
// 把注入逻辑封装成函数 function injectWishlistIcons() { const productItems = document.querySelectorAll('.product-item, .grid-item, .product-card, .col-product, .product-grid-item'); productItems.forEach(item => { // 重复代码省略,和上面的遍历逻辑一致 if (item.querySelector('.wishlist-icon')) return; let productId, variantId, productHandle; const dataContainer = item.querySelector('.wishlist-product-data'); if (dataContainer) { productId = dataContainer.dataset.productId; variantId = dataContainer.dataset.variantId; productHandle = dataContainer.dataset.productHandle; } else { const productLink = item.querySelector('a[href^="/products/"]'); if (productLink) productHandle = productLink.href.match(/\/products\/([^\/?]+)/)[1]; productId = item.dataset.productId || item.querySelector('[data-product-id]')?.dataset.productId; variantId = item.dataset.variantId || item.querySelector('[data-variant-id]')?.dataset.variantId; } if (!productId && !productHandle) return; const wishlistIcon = document.createElement('button'); wishlistIcon.className = 'wishlist-icon'; wishlistIcon.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>'; wishlistIcon.setAttribute('aria-label', '添加到心愿单'); wishlistIcon.addEventListener('click', function(e) { e.preventDefault(); console.log('添加到心愿单:', { productId, variantId, productHandle }); }); const imageContainer = item.querySelector('.product-image, .card-image, .product__img'); if (imageContainer) { imageContainer.style.position = 'relative'; Object.assign(wishlistIcon.style, { position: 'absolute', top: '10px', right: '10px', background: '#fff', border: 'none', borderRadius: '50%', padding: '8px', cursor: 'pointer', zIndex: 10 }); imageContainer.appendChild(wishlistIcon); } else { item.prepend(wishlistIcon); } }); } // 初始加载执行 injectWishlistIcons(); // 监听商品网格的DOM变化,处理动态加载的商品 const gridContainer = document.querySelector('.product-grid, .grid, .products'); if (gridContainer) { const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.addedNodes.length) injectWishlistIcons(); }); }); observer.observe(gridContainer, { childList: true, subtree: true }); }
内容的提问来源于stack exchange,提问作者Shine18
相关产品推荐
相关产品推荐

