product-comparison

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Product Comparison

产品对比

Overview

概述

Build a side-by-side product comparison feature where shoppers select 2–4 products and see their attributes in a sticky-header table. Attribute rows that are identical across all selected products can be hidden to reduce noise. The comparison state is stored in the URL so it can be shared or bookmarked.
构建一款并排产品对比功能,购物者可选择2–4款产品,在固定表头的表格中查看它们的属性。所有选中产品属性相同的行可被隐藏,以减少冗余信息。对比状态会存储在URL中,方便分享或添加书签。

When to Use This Skill

何时使用此功能

  • When selling products with many technical specifications (electronics, appliances, cameras)
  • When conversion research shows shoppers are considering multiple products before purchasing
  • When the product catalog has well-structured attribute data that lends itself to comparison
  • When building a B2B store where buyers need to justify purchase decisions to stakeholders
  • When implementing a "Compare" checkbox on product listing pages
  • 销售带有大量技术规格的产品时(电子产品、家电、相机等)
  • 转化研究显示购物者在购买前会考虑多款产品时
  • 产品目录拥有结构清晰、适合对比的属性数据时
  • 搭建B2B店铺,采购者需要向利益相关者证明购买决策合理性时
  • 在产品列表页实现「对比」复选框时

Core Instructions

核心操作步骤

Step 1: Determine the merchant's platform and choose the right approach

步骤1:确定商家平台并选择合适方案

PlatformRecommended ApproachWhy
ShopifyInstall Comparify or Product Compare app (both free tiers available)Shopify doesn't have built-in comparison; these apps add Compare buttons to product cards, a floating comparison tray, and a full comparison table page; they pull product metafields for spec data
WooCommerceInstall YITH WooCommerce Compare (free) or WooCommerce Products CompareYITH Compare is the most popular free option — adds Compare checkboxes to product cards, a comparison table page using WooCommerce product attributes, and a "show differences only" toggle
BigCommerceEnable the built-in Compare feature in Storefront → My Themes → Customize (Cornerstone theme)BigCommerce and Cornerstone include native product comparison — enable it in the Theme Editor; it uses product custom fields as comparison attributes
Custom / HeadlessBuild a URL-state comparison tray + comparison table page with your product attribute dataFull control over attribute display, difference highlighting, and table layout; see implementation below
平台推荐方案原因
Shopify安装ComparifyProduct Compare应用(均提供免费版)Shopify无内置对比功能;这些应用会在产品卡片上添加对比按钮、悬浮对比托盘和完整的对比表格页面;它们会提取产品元字段作为规格数据
WooCommerce安装YITH WooCommerce Compare(免费)或WooCommerce Products CompareYITH Compare是最受欢迎的免费选项——会在产品卡片上添加对比复选框,利用WooCommerce产品属性生成对比表格页面,并提供「仅显示差异」开关
BigCommerceStorefront → My Themes → Customize(Cornerstone主题)中启用内置的Compare功能BigCommerce和Cornerstone包含原生产品对比功能——在主题编辑器中启用即可;它使用产品自定义字段作为对比属性
自定义/无头电商利用产品属性数据构建基于URL状态的对比托盘 + 对比表格页面可完全控制属性展示、差异高亮和表格布局;实现方式见下文

Step 2: Enable and configure comparison on your platform

步骤2:在平台上启用并配置对比功能



Shopify

Shopify

Using Comparify app:
  1. Install Comparify – Product Comparison from the Shopify App Store (free tier available)
  2. In the app settings, select which product metafields to display as comparison rows (e.g., material, weight, dimensions, warranty)
    • If you haven't set up metafields yet: go to Settings → Custom data → Products and create metafields for your specs
  3. The app automatically adds a "Compare" checkbox to product cards in your collection pages
  4. Shoppers select up to 4 products, click "Compare" in the floating tray, and land on a full comparison table
  5. Configure which attributes appear as rows and in what order in the app's Table Settings
Preparing your product data:
  • Add spec data as product metafields (recommended) or in the product description with consistent formatting
  • For variant specs, add them as variant metafields (e.g., weight per size)

使用Comparify应用:
  1. 从Shopify应用商店安装Comparify – Product Comparison(提供免费版)
  2. 在应用设置中,选择要作为对比行展示的产品元字段(例如材质、重量、尺寸、保修)
    • 若尚未设置元字段:前往Settings → Custom data → Products,为产品规格创建元字段
  3. 应用会自动在商品集合页的产品卡片上添加「对比」复选框
  4. 购物者最多选择4款产品,点击悬浮托盘中的「对比」按钮,即可进入完整对比表格页面
  5. 在应用的Table Settings中配置显示的属性行及其顺序
准备产品数据:
  • 将规格数据添加为产品元字段(推荐),或在产品描述中使用统一格式填写
  • 对于变体规格,添加为变体元字段(例如不同尺寸对应的重量)

WooCommerce

WooCommerce

Using YITH WooCommerce Compare (free):
  1. Install and activate from WordPress.org
  2. Go to YITH → Compare → Settings
  3. Under Fields to compare, select which WooCommerce product attributes and custom fields to show as rows (e.g., Color, Material, Dimensions, Weight)
  4. Set Maximum products to 4
  5. Enable Highlight differences to visually call out rows where products differ
  6. The plugin adds a "Compare" button to product cards on your shop page and archives
  7. A floating comparison bar appears at the bottom of the page as shoppers add products
Preparing your product data:
  • Add comparison attributes via Products → Attributes — create attributes like "Material", "Warranty", "Compatible with" and assign values to each product
  • Products must use the same attribute names for comparison rows to align correctly

使用YITH WooCommerce Compare(免费版):
  1. 从WordPress.org安装并激活插件
  2. 前往YITH → Compare → Settings
  3. Fields to compare中,选择要作为对比行展示的WooCommerce产品属性和自定义字段(例如颜色、材质、尺寸、重量)
  4. Maximum products设置为4
  5. 启用Highlight differences,以视觉方式突出显示产品存在差异的行
  6. 插件会在商店页面和归档页的产品卡片上添加「对比」按钮
  7. 当购物者添加产品时,页面底部会显示悬浮对比栏
准备产品数据:
  • 通过Products → Attributes添加对比属性——创建「材质」「保修」「兼容设备」等属性,并为每个产品分配对应值
  • 产品必须使用相同的属性名称,才能确保对比行正确对齐

BigCommerce

BigCommerce

Built-in comparison (Cornerstone theme):
  1. Go to Storefront → My Themes → Customize
  2. Navigate to Global → Product Compare (or Category Page → Product Compare depending on your theme version)
  3. Toggle Enable product comparison to On
  4. Set the Maximum products (default is 4)
  5. Configure which Product Custom Fields appear as comparison rows in Products → Product Custom Fields settings
  6. Shoppers see a "Compare" checkbox on product cards; the floating compare tray appears automatically
Adding spec data:
  • Go to Products → [product] → Custom Fields and add name/value pairs (e.g., "Screen Size: 15.6 inches", "Battery Life: 10 hours")
  • Use the same field names across comparable products so they align in the comparison table

内置对比功能(Cornerstone主题):
  1. 前往Storefront → My Themes → Customize
  2. 导航至Global → Product Compare(或根据主题版本选择Category Page → Product Compare
  3. Enable product comparison切换为开启状态
  4. 设置Maximum products(默认值为4)
  5. Products → Product Custom Fields设置中配置要作为对比行展示的Product Custom Fields
  6. 购物者会在产品卡片上看到「对比」复选框;悬浮对比托盘会自动显示
添加规格数据:
  • 前往Products → [对应产品] → Custom Fields,添加名称/值对(例如「屏幕尺寸:15.6英寸」「续航时长:10小时」)
  • 同类产品使用相同的字段名称,确保它们在对比表格中正确对齐

Custom / Headless

自定义/无头电商

Comparison tray (floating bar as products are selected):
jsx
// ComparisonTray.jsx
export function ComparisonTray({ selectedProducts, onRemove, onClear }) {
  if (selectedProducts.length === 0) return null;

  const compareUrl = `/compare?${selectedProducts.map(p => `compare=${p.id}`).join('&')}`;

  return (
    <div className="comparison-tray" aria-live="polite" aria-label="Products selected for comparison">
      <div className="tray-products">
        {selectedProducts.map(product => (
          <div key={product.id} className="tray-product">
            <img src={product.image} alt={product.name} width="48" height="48" />
            <button onClick={() => onRemove(product.id)} aria-label={`Remove ${product.name} from comparison`}>&times;</button>
          </div>
        ))}
        {Array.from({ length: Math.max(0, 4 - selectedProducts.length) }).map((_, i) => (
          <div key={`empty-${i}`} className="tray-placeholder" aria-hidden="true">+</div>
        ))}
      </div>
      <div className="tray-actions">
        <a href={compareUrl} className="btn-primary" aria-disabled={selectedProducts.length < 2}>
          Compare ({selectedProducts.length})
        </a>
        <button onClick={onClear}>Clear all</button>
      </div>
    </div>
  );
}
Comparison table with sticky headers and difference highlighting:
jsx
export function ProductComparisonTable({ products, attributeGroups, showOnlyDifferences }) {
  function isRowIdentical(attrKey) {
    const values = products.map(p => p.attributes[attrKey]);
    return values.every(v => v === values[0]);
  }

  return (
    <div className="comparison-wrapper" style={{ overflowX: 'auto' }}>
      <table className="comparison-table">
        <caption className="sr-only">
          Side-by-side comparison of {products.map(p => p.name).join(', ')}
        </caption>
        <thead>
          <tr>
            <th scope="col" className="attr-col">Attribute</th>
            {products.map(product => (
              <th key={product.id} scope="col">
                <img src={product.image} alt={product.name} width="80" height="80" />
                <a href={product.url}>{product.name}</a>
                <strong>${product.price}</strong>
                <button className="btn-primary">Add to Cart</button>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {attributeGroups.map(group => (
            <>
              <tr key={`group-${group.label}`}>
                <th scope="rowgroup" colSpan={products.length + 1}>{group.label}</th>
              </tr>
              {group.attributes.map(attrKey => {
                if (showOnlyDifferences && isRowIdentical(attrKey)) return null;
                return (
                  <tr key={attrKey} className={isRowIdentical(attrKey) ? 'identical-row' : 'different-row'}>
                    <th scope="row">{attrKey.replace(/_/g, ' ')}</th>
                    {products.map(p => (
                      <td key={p.id}>{p.attributes[attrKey] ?? 'N/A'}</td>
                    ))}
                  </tr>
                );
              })}
            </>
          ))}
        </tbody>
      </table>
    </div>
  );
}
URL state for comparison (use
replaceState
to avoid polluting back-button history):
javascript
function toggleCompare(productId) {
  const params = new URLSearchParams(window.location.search);
  const current = params.getAll('compare');
  if (current.includes(productId)) {
    params.delete('compare');
    current.filter(id => id !== productId).forEach(id => params.append('compare', id));
  } else if (current.length < 4) {
    params.append('compare', productId);
  }
  window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`);
}
对比托盘(添加产品时显示的悬浮栏):
jsx
// ComparisonTray.jsx
export function ComparisonTray({ selectedProducts, onRemove, onClear }) {
  if (selectedProducts.length === 0) return null;

  const compareUrl = `/compare?${selectedProducts.map(p => `compare=${p.id}`).join('&')}`;

  return (
    <div className="comparison-tray" aria-live="polite" aria-label="Products selected for comparison">
      <div className="tray-products">
        {selectedProducts.map(product => (
          <div key={product.id} className="tray-product">
            <img src={product.image} alt={product.name} width="48" height="48" />
            <button onClick={() => onRemove(product.id)} aria-label={`Remove ${product.name} from comparison`}>&times;</button>
          </div>
        ))}
        {Array.from({ length: Math.max(0, 4 - selectedProducts.length) }).map((_, i) => (
          <div key={`empty-${i}`} className="tray-placeholder" aria-hidden="true">+</div>
        ))}
      </div>
      <div className="tray-actions">
        <a href={compareUrl} className="btn-primary" aria-disabled={selectedProducts.length < 2}>
          Compare ({selectedProducts.length})
        </a>
        <button onClick={onClear}>Clear all</button>
      </div>
    </div>
  );
}
带固定表头和差异高亮的对比表格:
jsx
export function ProductComparisonTable({ products, attributeGroups, showOnlyDifferences }) {
  function isRowIdentical(attrKey) {
    const values = products.map(p => p.attributes[attrKey]);
    return values.every(v => v === values[0]);
  }

  return (
    <div className="comparison-wrapper" style={{ overflowX: 'auto' }}>
      <table className="comparison-table">
        <caption className="sr-only">
          Side-by-side comparison of {products.map(p => p.name).join(', ')}
        </caption>
        <thead>
          <tr>
            <th scope="col" className="attr-col">Attribute</th>
            {products.map(product => (
              <th key={product.id} scope="col">
                <img src={product.image} alt={product.name} width="80" height="80" />
                <a href={product.url}>{product.name}</a>
                <strong>${product.price}</strong>
                <button className="btn-primary">Add to Cart</button>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {attributeGroups.map(group => (
            <>
              <tr key={`group-${group.label}`}>
                <th scope="rowgroup" colSpan={products.length + 1}>{group.label}</th>
              </tr>
              {group.attributes.map(attrKey => {
                if (showOnlyDifferences && isRowIdentical(attrKey)) return null;
                return (
                  <tr key={attrKey} className={isRowIdentical(attrKey) ? 'identical-row' : 'different-row'}>
                    <th scope="row">{attrKey.replace(/_/g, ' ')}</th>
                    {products.map(p => (
                      <td key={p.id}>{p.attributes[attrKey] ?? 'N/A'}</td>
                    ))}
                  </tr>
                );
              })}
            </>
          ))}
        </tbody>
      </table>
    </div>
  );
}
对比功能的URL状态管理(使用
replaceState
避免污染返回按钮历史):
javascript
function toggleCompare(productId) {
  const params = new URLSearchParams(window.location.search);
  const current = params.getAll('compare');
  if (current.includes(productId)) {
    params.delete('compare');
    current.filter(id => id !== productId).forEach(id => params.append('compare', id));
  } else if (current.length < 4) {
    params.append('compare', productId);
  }
  window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`);
}

Best Practices

最佳实践

  • Limit comparison to 2–4 products — more than 4 columns breaks table layout on most screens; enforce this in the UI
  • Group attributes by category — organize specs into groups (Display, Performance, Battery) to prevent a 50-row flat table
  • Offer "show differences only" toggle — rows where all products share the same value add noise; provide an easy toggle
  • Make the table horizontally scrollable on mobile — use
    overflow-x: auto
    on a wrapper; never hide columns to fit small screens
  • Pre-populate from listing page — when a shopper clicks "Compare Now" after selecting items on the PLP, navigate with IDs in the URL
  • Use consistent attribute naming — specs across products must use identical field names (e.g., "Screen Size" not sometimes "Display Size") for table rows to align
  • 限制对比数量为2–4款产品——超过4列会在大多数屏幕上破坏表格布局;在UI中强制执行此限制
  • 按类别分组属性——将规格分为不同组别(显示、性能、电池等),避免出现50行的扁平表格
  • 提供「仅显示差异」开关——所有产品属性相同的行会增加冗余信息,提供便捷开关隐藏此类行
  • 在移动端使表格可横向滚动——在容器上使用
    overflow-x: auto
    ;切勿为适配小屏幕而隐藏列
  • 从列表页预填充对比——当购物者在产品列表页选择商品后点击「立即对比」,通过URL携带产品ID跳转
  • 使用统一的属性命名——不同产品的规格必须使用完全相同的字段名称(例如统一用「屏幕尺寸」,不要有时用「显示屏尺寸」),确保表格行正确对齐

Common Pitfalls

常见问题与解决方案

ProblemSolution
Table overflows on mobileWrap in a scrollable container; use
position: sticky
for the first column (attribute labels)
Attributes missing for some productsUse "N/A" as the value — never skip the cell as it breaks column alignment
Comparison tray covers page contentAdd
padding-bottom
to the page body equal to the tray height when the tray is visible
Products have different attribute setsNormalize attribute keys across all compared products; fill missing values with
null
问题解决方案
表格在移动端溢出将表格包裹在可滚动容器中;为第一列(属性标签)设置
position: sticky
部分产品缺少属性使用「N/A」作为值——切勿跳过单元格,否则会破坏列对齐
对比托盘遮挡页面内容当托盘显示时,为页面主体添加与托盘高度相等的
padding-bottom
产品属性集合不同统一所有对比产品的属性键;缺失值用
null
填充

Related Skills

相关功能

  • @product-page-design
  • @faceted-navigation
  • @accessibility-commerce
  • @recently-viewed-products
  • @product-page-design
  • @faceted-navigation
  • @accessibility-commerce
  • @recently-viewed-products