【Shopify】バリエーションボタンを「即納」「受注生産」「SOLD OUT」の3種類で色分けして表示&カートボタンの文言も選んだバリエーションによって自由自在に変更する!

Shopify
Shopify

以下の要件を実現するのに、けっこう苦労したので、その備忘録と、同じような苦労を誰かがしそうなときの助けになればと思い記述いたします。

要件

  • 商品詳細ページでバリエーションが「在庫がゼロになっても販売を続ける」としたときにカートボタンの文言を「受注生産で注文する」に変更しボタンの色も変更する
  • バリエーションボタンの色も、「即納」「受注生産」「SOLD OUT」の3種類で色分けして表示する

通常のテーマでは、バリエーション(バリアントとも呼ばれていてどちらが正解なのか不明)とカートインボタン(カートに入れるボタン)は、2種類の色分けしかなく、カートインボタンの文言は、翻訳(「テーマのコンテンツ」)で編集可能な、「カートに入れる」と「売り切れ(SOLD OUT)」のいずれかの表現しかない。

この度、商品のSKUごとに設定できる「在庫切れの時でも販売を続ける」(ステータス名でいうと”inventory_policy: continue”)にし、在庫数ゼロ以下でも販売。

「在庫がゼロになっても販売を続ける」をオンにして販売することで、「受注商品」として販売されるようにしたい。バリエーションボタンおよびカートインボタンが、通常の即納商品のカラー(ここでは黒)でもSOLD OUT時の薄いグレーのカラーでもなく、赤にしたいとする。

受注生産する数量は、注文が入っていくと、ゼロからマイナスに数が増えていくので、そのマイナスの数が受注生産しなければいけない数量、というわけだ。

参考になった記事

https://community.shopify.com/c/技術的なq-a/商品詳細ページのバリエーション画像について/m-p/1681594

実現方法

バリエーションのボタンのsnippetにて、各バリエーションの「受注生産」「SOLD OUT」のステータスのクラスを出力させる。(「即納」はクラスなし=デフォルトの表示とする)

このバリエーションのクラス出力ができれば、バリエーション選択時のテーマのリスナーイベント内で、カートボタンの表示変更を行う。

テーマのリスナーイベント内で行うことで、バリエーションを変更したさいにも非同期に変更を再現してくれる。

”product.selected_or_first_available_variant”の方法だと、ページロード時の状態からカートインボタンのテキストや色を動的に変更できない。

※以下のようなコードはページを開いたあとのみしか適用されず、バリエーションをコロコロ変更してもイベントリスナーできない。

{% assign current_variant = product.selected_or_first_available_variant %}

実施内容

1) DAWNのsnippetの”product-variant-options.liquid”の以下のコードを編集

変更前

{%- for value in option.values -%}
  {%- liquid
    assign option_disabled = true・・・(略)

変更後

{%- for value in option.values -%}
  {% comment %}ここからが追記部分{% endcomment %}
  {% assign variant = product.variants | where: "title", value | first %}
  {% assign label_class = "" %}
  {%- if variant.inventory_quantity <= 0 -%}
    {%- if variant.inventory_policy == "continue" -%}
      {%- assign label_class = "out-of-stock-continue" -%}
    {%- else -%}
      {%- assign label_class = "out-of-stock-stop" -%}
    {%- endif -%}
  {%- endif -%}
  {% comment %}ここまでが追記部分{% endcomment %}
  
  {%- liquid
    assign option_disabled = true・・・(略)

こうすることで、バリエーションボタンのクラスに、 “out-of-stock-continue”か”out-of-stock-stop”が追加出力されるようにする。

こうするとバリエーションボタンは「即納」「受注生産」「SOLD OUT」のステータスの違いを見て色を分けて表示できるようになる。

2) DAWNのassetの”product-info.js”の以下のコードを編集

変更前

connectedCallback() {
  if (!this.input) return;
  this.quantityForm = this.querySelector('.product-form__quantity');
  if (!this.quantityForm) return;
  this.setQuantityBoundries();
  if (!this.dataset.originalSection) {
    this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, this.fetchQuantityRules.bind(this));
  }
  this.variantChangeUnsubscriber = subscribe(PUB_SUB_EVENTS.variantChange, (event) => {
    const sectionId = this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section;
    if (event.data.sectionId !== sectionId) return;
    this.updateQuantityRules(event.data.sectionId, event.data.html);
    this.setQuantityBoundries();
  });
}

変更後※このあとさらに最終形があります。

connectedCallback() {
  if (!this.input) return;
  this.quantityForm = this.querySelector('.product-form__quantity');
  if (!this.quantityForm) return;
  this.setQuantityBoundries();
  if (!this.dataset.originalSection) {
    this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, this.fetchQuantityRules.bind(this));
  }
  this.variantChangeUnsubscriber = subscribe(PUB_SUB_EVENTS.variantChange, (event) => {
    const sectionId = this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section;
    if (event.data.sectionId !== sectionId) return;
    this.updateQuantityRules(event.data.sectionId, event.data.html);
    this.setQuantityBoundries();
  });

  this.variantPicker = this.querySelector('variant-selects'); // バリエーションピッカーのセレクターを適宜調整
  //「数量セレクター」のブロックを非表示にすると動作しないので注意
  if (this.variantPicker) {
    this.variantPicker.addEventListener('change', (event) => {
      const selectedVariantClassName = event.target.className;
      //window.alert(selectedVariantClassName);
      if (selectedVariantClassName === 'out-of-stock-continue'){
        // 在庫ポリシーが'continueで在庫数が0以下の場合の処理
        this.submitButton.textContent = '受注生産で注文する';
        document.querySelector('.product-form__submit').classList.add('bg_red');
        document.querySelector('.product-form__submit').classList.remove('bg_black', 'bg_soldout');
      } else if(selectedVariantClassName === 'out-of-stock-stop') {
        // 在庫ポリシーが'continue以外で在庫数が0以下の場合の処理
        this.submitButton.textContent = 'SOLD OUT';
        document.querySelector('.product-form__submit').classList.add('bg_soldout');
        document.querySelector('.product-form__submit').classList.remove('bg_black', 'bg_red');            
      } else {
        // その他の場合の処理
        this.submitButton.textContent = 'カートに追加する';
        document.querySelector('.product-form__submit').classList.add('bg_black');
        document.querySelector('.product-form__submit').classList.remove('bg_red', 'bg_soldout');
      }
    });
  }
}
}

1の手順でバリエーションにクラス名をつけられたので、variantPickerの’change’イベントをリスナーし、割り当てたクラス名のバリエーションが選択されたらボタンの文言を変更し、ボタンに追加クラスを付与。

こうして、カートインボタンも「即納」「受注生産」「SOLD OUT」のステータスの違いを見て色を分けて表示できるようになった。

追記) 一部コードの訂正

上記コードで、

this.submitButton.textContent = '受注生産で注文する';

のスクリプトを実行すると、カートインボタンが機能しなくなる(表示は変わりますが、押しても機能が実行されなくなる)という事象になりましたので、

カートインボタンのテキストも、ボタンに付与したクラスを利用し、cssのハック的手法でテキストを変更する仕様にした。

product-info.js内のコード該当箇所修正後(修正版)

connectedCallback() {
  if (!this.input) return;
  this.quantityForm = this.querySelector('.product-form__quantity');
  if (!this.quantityForm) return;
  this.setQuantityBoundries();
  if (!this.dataset.originalSection) {
    this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, this.fetchQuantityRules.bind(this));
  }
  this.variantChangeUnsubscriber = subscribe(PUB_SUB_EVENTS.variantChange, (event) => {
    const sectionId = this.dataset.originalSection ? this.dataset.originalSection : this.dataset.section;
    if (event.data.sectionId !== sectionId) return;
    this.updateQuantityRules(event.data.sectionId, event.data.html);
    this.setQuantityBoundries();
  });

  this.variantPicker = this.querySelector('variant-selects'); // バリエーションピッカーのセレクターを適宜調整
  //「数量セレクター」のブロックを非表示にすると動作しないので注意
  if (this.variantPicker) {
    this.variantPicker.addEventListener('change', (event) => {
      const selectedVariantClassName = event.target.className;
      //window.alert(selectedVariantClassName);
      if (selectedVariantClassName === 'out-of-stock-continue'){
        // 在庫ポリシーが'continueで在庫数が0以下の場合の処理
        document.querySelector('.product-form__submit').classList.add('bg_red');
        document.querySelector('.product-form__submit').classList.remove('bg_black', 'bg_soldout');
      } else if(selectedVariantClassName === 'out-of-stock-stop') {
        // 在庫ポリシーが'continue以外で在庫数が0以下の場合の処理
        document.querySelector('.product-form__submit').classList.add('bg_soldout');
        document.querySelector('.product-form__submit').classList.remove('bg_black', 'bg_red');            
      } else {
        // その他の場合の処理
        document.querySelector('.product-form__submit').classList.add('bg_black');
        document.querySelector('.product-form__submit').classList.remove('bg_red', 'bg_soldout');
      }
    });
  }
}
}

そして、以下のcssをどこかに追加。これでカートインボタンも問題なく機能実行でき解消できました。

.product-form__submit.bg_red>span {
  font-size: 0;
}
.product-form__submit.bg_red>span:after {
  content: '受注生産で注文する';
  font-size: 1.4rem;
}
.product-form__submit.bg_black>span {
  font-size: 0;
}
.product-form__submit.bg_black>span:after {
  content: 'カートに追加する';
  font-size: 1.4rem;
}
.product-form__submit.bg_soldout>span {
  font-size: 0;
}
.product-form__submit.bg_soldout>span:after {
  content: 'SOLD OUT';
  font-size: 1.4rem;
}

Webサイト制作を依頼したい方へ

PRYTHM WORKS(プリズムワークス)は、東京都墨田区、東京スカイツリーのふもとにあるWebコンテンツ制作事務所です。

華々しいおしゃれなECサイトをはじめ、アンダーグラウンドな案件まで幅広くご依頼をいただき、どんな案件でも真心・丁寧・楽しくを理念に掲げて制作しております!

作りたいサービスはあるけど、まずは費用感が知りたい!という方も、まずはお問い合わせください!

mail@prytymworks.tokyo

PRYTHM WORKSが手掛ける仕事の一部をご紹介します。

制作のご依頼者様用 費用かんたんお見積もりフォーム

おそらく本記事を読まれるのは、制作の現場の、法人またはフリーの、プロデューサーの方、ディレクターの方、エンジニアの方がほとんどかと存じます。
いつもおつかれさまです!そして本記事をお読みいただきありがとうございます。
紹介した記事の内容について、またはその他制作のご依頼について、以下のフォームより簡易お見積もりができます!
試算だけならフォーム送信しなくてもできますので、ぜひ試しにいかがでしょう?

お仕事をご希望の制作者様用 お問合せフォーム

また、まずはライトなご相談から…ということであれば、こちらのコンタクトフォームからお気軽にどうぞ!ご縁を大切にしてご返信いたします!

    タイトルとURLをコピーしました