Liquid Snippets by ALSEL
🔍 コレクション/検索中級

コレクション商品のバリアント付き一覧表

コレクション内の全商品をバリアント単位の表形式で表示し、各行から直接カートに追加できるセクション。商品画像、説明、SKU、オプション、価格を一覧化して、B2B 向け注文フローや大量購入シーンに対応する。

用途
コレクションページで複数バリアントを持つ商品(色・サイズ等)を整理して表示したいとき。特に卸売・業務用など、複数バリアントからの一括選定と追加が必要なシーンに最適。
設置場所
sections/single-collection.liquid に配置し、テーマカスタマイザーでコレクション選択セクションとして呼び出す。collection.liquid テンプレートの本体部分に `{% section 'single-collection' %}` で組み込む。
注意点
商品オプションが3個以上ある場合は、table 構造が横に膨らんで可読性が低下するため、スクロール対応や折り畳みに書き換える必要がある。バリアント画像ではなく商品の featured_image のみ表示されるため、各バリアントに対応する別画像が必要なら metafield で画像 URL を持たせて追加実装する。重量(weight)フィールドが未設定だと 'N/A' が表示されるため、商品作成時に各バリアントの重量を正確に入力しておく前提。
タグ:collectionvariantproduct-tablecart-integrationbulk-add

コード

196 行 / liquid
{% assign collection_products = section.settings.collection.products %}
{% if collection_products.size > 0 %}
  <h1 class="sticky-header">
    All Categories >
    {{ section.settings.collection.title }} ({{ collection_products.size }}
    {{ collection_products.size | pluralize: 'product', 'products' }})
  </h1>

  <div class="product-collection" style="font-family: Arial, sans-serif;">
    {% for product in collection_products %}
      <div class="product-container">
        <span id="{{ product.handle }}" class="hash-handle"></span>
        <h3 class="product-title">{{ product.title }}</h3>
        <p class="product-description">{{ product.description | strip_html | truncatewords: 30 }}</p>

        <div class="product-details">
          <div class="image-placeholder">
            {% if product.featured_image %}
              {{ product.featured_image | image_url: width: 100 | image_tag: alt: product.featured_image.alt }}
            {% endif %}
          </div>
          <table class="variant-table">
            <thead>
              <tr>
                <th class="desktop-only">SKU</th>
                <th class="desktop-only">{{ product.options[0] }}</th>
                {% if product.options[1] %}
                  <th class="desktop-only">{{ product.options[1] }}</th>
                {% endif %}
                <th>Model</th>
                <th class="desktop-only">Net Weight</th>
                <th>Each</th>
                <th>Buy</th>
              </tr>
            </thead>
            <tbody>
              {% for variant in product.variants %}
                <tr class="variant-row">
                  <td class="desktop-only">{{ variant.sku }}</td>
                  <td class="desktop-only">{{ variant.option1 }}</td>
                  {% if variant.option2 %}
                    <td class="desktop-only">{{ variant.option2 }}</td>
                  {% endif %}
                  <td>{{ variant.title }}</td>
                  <td class="desktop-only">{{ variant.weight | default: 'N/A' }}g</td>

                  <td>{{ variant.price | money }}</td>
                  <td>
                    <form class="add-to-cart-form" method="post" action="/cart/add">
                      <input type="hidden" name="id" value="{{ variant.id }}">
                      <input min="1" type="number" id="quantity" name="quantity" value="1">
                      <input type="submit" value="Add to cart" class="add-button">
                      <input type="submit" value="Added!" class="added-button" disabled>
                    </form>
                  </td>
                </tr>
              {% endfor %}
            </tbody>
          </table>
        </div>
      </div>
    {% endfor %}
  </div>
{% endif %}

{% stylesheet %}
  .desktop-only {
    display: none;
  }
  @media (min-width: 768px) {
    .desktop-only {
      display: table-cell;
    }
  }
  .product-collection {
    font-family: Arial, sans-serif;
  }
  .collection-title {
    font-size: 24px;
    margin-bottom: 10px;
  }
  .product-count {
    font-size: 14px;
    margin-bottom: 20px;
  }
  .product-container {
    margin-bottom: 20px;
    padding: 10px;
    position: relative;
  }
  .hash-handle {
    visibility: hidden;
    top: -35px;
    position: absolute;
  }
  .product-title {
    font-size: 18px;
    color: green;
  }
  .product-description {
    font-size: 12px;
  }
  .product-details {
    display: flex;
    align-items: center;
    margin-bottom: 10px;
  }
  .image-placeholder {
    width: 100px;
    height: 100px;
    margin-right: 10px;
  }
  .image-placeholder img {
    width: 100%;
    height: auto;
    object-fit: cover;
  }
  .variant-table {
    width: 100%;
    border-collapse: collapse;
  }
  th,
  td {
    text-align: left; /* Left-align headers and cells */
    padding: 4px; /* Tighter line height */
    font-size: 12px;
  }
  .variant-row:hover {
    background-color: #fff5b1; /* Pale yellow background on hover */
  }

  .add-to-cart-form input[type='number'] {
    width: 2rem;
  }

  .added-button {
    display: none;
  }
{% endstylesheet %}

{% javascript %}
  document.addEventListener('DOMContentLoaded', function () {
    // Dynamically add items to the cart without reloading the page
    document.querySelectorAll('.add-to-cart-form').forEach((form) => {
      form.addEventListener('submit', function (event) {
        event.preventDefault();

        const formData = new FormData(form);

        fetch(window.Shopify.routes.root + 'cart/add.js', {
          method: 'POST',
          body: formData,
        })
          .then((response) => response.json())
          .then((data) => {
            form.querySelector('.add-button').style.display = 'none';
            form.querySelector('.added-button').style.display = 'inline-block';
            setTimeout(() => {
              form.querySelector('.added-button').style.display = 'none';
              form.querySelector('.add-button').style.display = 'inline-block';
            }, 2000);
            // Update the cart item count in the page header
            fetch(window.Shopify.routes.root + 'cart.js', {
              method: 'GET',
            })
              .then((response) => response.json())
              .then((cart) => {
                document.querySelector('#cart-item-count').textContent = cart.items.length;
              });
          })
          .catch((error) => {
            console.error('Error adding item to cart:', error);
          });
      });
    });
  });
{% endjavascript %}

{% schema %}
{
  "name": "Single Collection View",
  "class": "section",
  "tag": "section",
  "disabled_on": {
    "groups": ["header", "footer"]
  },
  "settings": [
    {
      "type": "collection",
      "id": "collection",
      "label": "Collection"
    }
  ]
}
{% endschema %}

出典・ライセンス

License:
MIT

このコードは Shopify 著作の MIT ライセンスソースです。 原本の著作権は Shopify が保有します。日本語訳は ALSEL によるものです。

関連項目

🔍 コレクション/検索初級

ページネーションの表示

ページネーションUI(前へ・次へ、ページ番号)を paginate オブジェクトから描画するスニペット。デフォルトのページネーション形式で複数ページに分割したコンテンツをナビゲートできる。

📁 shopify-code-snippets·MIT·6
🔍 コレクション/検索初級

ページネーションの現在位置表示

コレクションや検索結果の一覧ページで、現在のページ番号と全ページ数を表示する。Shopify の多言語対応翻訳キーを使用して、言語ごとに自動で表記を切り替える。

📁 shopify-code-snippets·MIT·6
🔍 コレクション/検索中級

検索結果のペジネーション比較

検索結果を paginate タグで複数パターン分割して表示するテンプレート。デフォルトペジネーションと window_size:1 オプション指定の両方の書き方を示しており、ページネーション動作の違いを検証するサンプルコード。

📁 theme-tools·MIT·12
🔍 コレクション/検索初級

検索結果のページネーション設定

検索結果を paginate タグでページ分割し、複数の window_size 設定を並べて出力する方法。28件単位のペーグネーションと、ウィンドウサイズを明示的に指定した設定の2パターンを示している。

📁 theme-tools·MIT·12
🔍 コレクション/検索初級

コレクションページの商品ギャラリー

コレクション内の商品を3列のテーブルレイアウトで表示するテンプレート。各商品にサムネイル、タイトル、価格(セール時は定価を取消線)を表示し、ページネーションに対応する。

📁 liquid·MIT·20
🔍 コレクション/検索初級

コレクションページの商品一覧

コレクション内の商品を一覧表示し、商品画像・タイトル・説明文・価格帯をカード状に並べる。ページネーション機能で20件ごとに分割表示する。

📁 liquid·MIT·23