以下の要件を実現するのに、けっこう苦労したので、その備忘録と、同じような苦労を誰かがしそうなときの助けになればと思い記述いたします。
要件
- 商品詳細ページでバリエーションが「在庫がゼロになっても販売を続ける」としたときにカートボタンの文言を「受注生産で注文する」に変更しボタンの色も変更する
- バリエーションボタンの色も、「即納」「受注生産」「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;
}