Dawn主题如何点击标签动态切换商品集并保留Liquid配置?
问题背景
我在Shopify的Dawn主题中使用Featured Collection区块,想添加「男款衬衫」「男款裤子」这类标签,点击时无需刷新页面就能动态加载对应商品集的商品。尝试用Section Rendering API,请求路径为/collections/{{handle}}?section_id={{section.id}},但返回的HTML里没有包含完整Liquid配置的商品卡片。
当前使用的JS代码:
const tabButtons = document.querySelectorAll('.collection-tab-btn'); const productGrid = document.getElementById('Slider-{{ section.id }}'); tabButtons.forEach(button => { button.addEventListener('click', function() { const collectionHandle = this.getAttribute('data-collection-handle'); const sectionId = this.getAttribute('data-section-id'); fetch(`/collections/${collectionHandle}?section_id=${sectionId}`) .then(response => response.text()) .then(html => { console.log(html); // 返回的HTML没有预期的商品卡片 }); }); });
核心需求:点击标签时,商品网格更新为对应商品集的商品,同时保留card-product代码片段的所有配置(lazy_load、show_vendor、show_rating、image_shape、quick_add)。目前获取的HTML仅包含部分商品信息,缺失代码片段数据;用JSON接口也无法在JS中渲染所有Liquid代码片段特性。
解决方案
一、修正Section Rendering API的请求逻辑
Dawn主题的Featured Collection区块渲染时依赖自身配置参数,直接请求/collections/{{handle}}?section_id={{section.id}}只会返回基础collection模板内容,不会复用原区块配置。需要调整区块的Liquid文件,让API能识别并复用原配置:
- 修改
sections/featured-collection.liquid文件
在文件顶部添加Section Rendering支持逻辑,确保API请求时自动使用目标collection的商品,同时保留原区块配置:
{% if request.path contains '/collections/' and section_id == section.id %} {% assign products = collection.products %} {% assign grid_settings = section.settings %} {% else %} {% assign products = section.settings.products %} {% assign grid_settings = section.settings %} {% endif %}
- 确保
card-product代码片段接收完整配置
在渲染商品卡片的部分,把所有配置参数传递给card-product:
{% render 'card-product', product: product, lazy_load: grid_settings.lazy_load, show_vendor: grid_settings.show_vendor, show_rating: grid_settings.show_rating, image_shape: grid_settings.image_shape, quick_add: grid_settings.quick_add %}
二、正确替换商品网格内容并初始化交互
返回的HTML中,商品网格内容嵌套在目标容器内,需要提取对应部分替换,同时重新初始化Dawn主题的交互逻辑(懒加载、滑块、快速添加等):
const tabButtons = document.querySelectorAll('.collection-tab-btn'); const productGrid = document.getElementById('Slider-{{ section.id }}'); tabButtons.forEach(button => { button.addEventListener('click', function() { const collectionHandle = this.getAttribute('data-collection-handle'); const sectionId = this.getAttribute('data-section-id'); // 添加加载状态 productGrid.classList.add('loading'); fetch(`/collections/${collectionHandle}?section_id=${sectionId}`) .then(response => response.text()) .then(html => { // 创建临时DOM提取目标容器内容 const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; const newProductGrid = tempDiv.querySelector(`#Slider-${sectionId}`); if (newProductGrid) { productGrid.innerHTML = newProductGrid.innerHTML; // 重新初始化滑块组件 if (window.Slideshow) { new window.Slideshow(productGrid); } // 触发懒加载 document.querySelectorAll('.lazyload').forEach(img => { img.classList.replace('lazyload', 'lazyloaded'); }); // 重新绑定快速添加事件(Dawn主题默认逻辑) document.querySelectorAll('.quick-add').forEach(btn => { btn.addEventListener('click', window.quickAdd); }); } productGrid.classList.remove('loading'); }) .catch(err => { console.error('加载商品集失败:', err); productGrid.classList.remove('loading'); }); }); });
三、替代方案:AJAX请求JSON+预渲染模板
如果Section Rendering API仍有问题,可采用JSON数据+预渲染模板的方式:
- 添加隐藏的商品卡片模板
在featured-collection.liquid中添加预渲染模板容器:
{% comment %} 预渲染商品卡片模板,用于AJAX动态渲染 {% endcomment %} <div id="product-card-template" style="display: none;"> {% render 'card-product', product: null, lazy_load: section.settings.lazy_load, show_vendor: section.settings.show_vendor, show_rating: section.settings.show_rating, image_shape: section.settings.image_shape, quick_add: section.settings.quick_add %} </div>
- 通过JSON接口加载并渲染商品
tabButtons.forEach(button => { button.addEventListener('click', function() { const collectionHandle = this.getAttribute('data-collection-handle'); productGrid.classList.add('loading'); fetch(`/collections/${collectionHandle}/products.json`) .then(response => response.json()) .then(data => { const template = document.getElementById('product-card-template').innerHTML; let html = ''; data.products.forEach(product => { // 替换模板中的Liquid变量为JSON数据 let cardHtml = template .replace(/{{ product.title }}/g, product.title) .replace(/{{ product.price | money }}/g, product.price_range.minimum_price.amount) .replace(/{{ product.featured_image.src | img_url: 'medium' }}/g, product.featured_image.src) .replace(/{{ product.url }}/g, `/products/${product.handle}`); // 根据card-product的变量补充更多替换规则 html += cardHtml; }); productGrid.innerHTML = html; // 重新初始化交互逻辑(同方案二) productGrid.classList.remove('loading'); }); }); });
内容的提问来源于stack exchange,提问作者jubaer hossain maruf

