同じ要件の案件だと、これまではアプリを使用して実装していましたが、この度アプリなしで実装できたので、その備忘録として。
やりたいことはこれ!
これをしたくてもスライダーとバリアント選択同期が対応していない古いテーマの部分改修案件で、アプリを使用せずに実装したい!という時や、新テーマだけど対応していないテーマの部分改修案件で、アプリを使用せずに実装したい!という時に、必ずぶち当たる問題かと存じますので、その参考にどうぞ。
前提の手順
1.「商品管理」の画面で各バリアントの画像に、対応する商品画像を割り当て
以下の画像アイコンをクリックすると、画像を登録できる(キャプチャは登録済の状態)
2. 画像にAlt情報を「バリアントのカラー名と同じ名称」で保存しておく
登録したメディアの画像をクリックすると、以下の画面になりますので、ここでALTテキストを設定できます。
ここに以下のように、バリアント名で付けるのと一字一句同じ名前をつけてください。頭文字が大文字なら必ず大文字に。
これは通常のバリアント=スライダー同期の手順と違います。ですが、今回、この画像のALT情報を参照する仕様にするために必須ですので、お忘れなきよう。
「仕様が面倒」?
いえいえ、画像にALT情報を設定することは普通に推奨なので、一石二鳥ですよ!(って考えることにしましょ!w)
ちなみに、本件は古いテーマ(Minimal)のストアに対し改修をかけたものなのですが、最近のテーマだとカスマタイズなしでも、標準機能でできるものもあるかもしれませんので、最新でかつ、癖のない、機能豊富なテーマがあれば、それを選ぶ方が早いかと思います!
参考になった記事
参考になった記事1
この質問者が知りたいことや、それを質問したときに返ってくるイージーな回答の具体が僕の状況とそっくりで、誰も明快に答えてくれない、なんとも残念な状態であることも似ていた。
その中で最後の方でようやく辿り着くコードが参考になった。
参考になった記事2
頭いい!と思った。大変ありがたいことに、自分の実装にも使える考えだった。
記事抜粋:
商品画像にある代替テキスト(image.alt)にskuをいれ、表示するvariantのskuとimage.altが一致する場合の画像を表示させることによって画像の出しわけができました。
バリアントの色を変更したら表示させる画像も変更させたい
{%- assign current_variant = product.selected_or_first_available_variant -%}
{% for image in product.images %}
{% if current_variant.sku == image.alt%}
<img src="" >
{% endif %}
{% endfor %}
↑ただこれだとページ更新しないと変わらない。
(僕の環境では実際にページ更新しないとサムネイルが変化しない挙動になった)
ということで使えませんでした。ですが、この「画像のAlt情報にカラー名を保存し、それを参照する」という発想が、今回の改修の重要なポイントになりました。
参考にならなかった記事
コアjsを追加することで動くようにしたいみたいだが、僕の案件のカスタムされたMinimalには追加しても動かなかった。。
仕様を実現するためにやったこと
- バリアント選択のコードを編集し、登録したバリアント画像が表示されるようにする
- 1で作成したバリアント画像をクリックしたときに、対応するスライダー画像がスライドして表示されるようにする
です。順に解説します。
1. バリアント選択のコードを編集し、登録したバリアント画像が表示されるようにする
product-template.liquidとか、
main-product.liquidと
書かれているsectionファイルの中で、
{{ option.name }}や{%- for value in option.values -%}
で検索してみよう。
そのあたりにバリアントの選択をするコードを見つける。
ここのひとまとまりのブロックを、以下のように変更。
{% if option.name == 'Size' %} <!-- ’Size’というバリアントは、既存の表示でよいのでまず分岐のはじめを既存の表示にする -->
<legend class="form__label">{{ option.name }}</legend>
{%- for value in option.values -%}
<input type="radio" id="{{ section.id }}-{{ option.name }}-{{ forloop.index0 }}"
name="{{ option.name }}"
value="{{ value | escape }}"
form="product-form-{{ section.id }}"
{% if option.selected_value == value %}checked{% endif %}
>
<label for="{{ section.id }}-{{ option.name }}-{{ forloop.index0 }}">
{{ value }}
</label>
{%- endfor -%}
{% else %}<!-- 'Size'以外のバリアント、つまりこの例では'Color'の際は以下の分岐 -->
<label>{{option.name}}</label>
{%assign index = forloop.index %}
<div class="variant-swatches">
{% for value in option.values %} <!-- バリアント画像を表示 -->
<input class="single-option-selector-{{ section.id }}" id="color-{{forloop.index}}" type="radio" name="color" value="{{ value | escape }}" data-index="option{{index}}" {% if option.selected_value == value %}checked{% endif %}/>
<label for="color-{{forloop.index}}">
{% comment %} {{ value }} {% endcomment %}
<img src="{{ product.variants[forloop.index0].featured_media | img_url: "small" }}"/> <!-- バリアント画像を表示 -->
<p>{{ value | escape }}</p> <!-- バリアント名を表示 -->
</label>
{% endfor %}
</div>
{% endif %}
1で作成したバリアント画像をクリックしたときに、対応するスライダー画像がスライドして表示されるようにする
つぎに、1で作成したバリアント画像をクリックしたときに、対応するスライダー画像がスライドして表示されるようにします。
これは色々なやり方が紹介されていますが、シンプルに考えると以下の実装が早かったので紹介します。
まず、バリアント画像とスライダー画像に共通で何かしらのカラー情報を持たせる。それができれば、
flickityというスライダーをすでにストアに設置していたので、flickityのapiで、
任意のスライダーを、“スライダー外にある要素”による複数のボタンから、”スライド表示を制御できるAPI”を使います。
↓ まさにこのイメージ。1~5のボタンが、バリアントのサムネイル画像になればいい。
See the Pen Flickity – select method by Dave DeSandro (@desandro) on CodePen.
上記のCodePenからCSSを除くHTML、JSをストアに設置してみると、確かに動作が確認できました。
余談ですが、今更またshopifyの仕様の重要な落とし穴にはまって、今日も何時間も無駄にしました。sectionファイルにcssやjpをインラインで記述するさいに、
{% style %}
{% endstyle %}
{% javascript %}
{% endjavascript %}
のようなタグを使うようにsectionファイルの雛形が作成された際に上記タグが備わっているのですが、これが曲者。。
強力なキャッシュがかかるので、一度プレビューしたあとにこのタグの中で編集しても、スーパーリロード(キャッシュクリア)では更新されない。
この仕様を忘れ、長時間なんのコードが悪くて反映がいっこうにされないのかを悩んでしまった。。
「product.imageのすべての画像にカラー名でalt情報を付ける」ということを冒頭の「前提」でしています。
なので、これを利用し、product-template.liquid内の、メインスライダーを表示している以下のコード
{% for image in product.images %}
<div data-image-id="{{ image.id }}" class="carousel-cell"><img src="{{ image.src | img_url: '900x' }}" alt="{{ image.alt | escape }}" ></div>
{% endfor %}
に追記し、liquidでproduct.imageのすべての画像のalt情報を取得して変数に格納します。
{{ image.alt | escape }}
でaltを取得しているので
以下のように変更
<script>
var mainSlide = [];
</script>
{% for image in product.images %}
<div data-image-id="{{ image.id }}" class="carousel-cell"><img src="{{ image.src | img_url: '900x' }}" alt="{{ image.alt | escape }}" >
<script>
mainSlide.push('{{ image.alt | escape }}');
console.log( mainSlide );
</script>
</div>
{% endfor %}
これで以下のように配列に格納される
変数名 | 値 |
mainSlide[0] | Black |
mainSlide[1] | Green |
mainSlide[2] | Blue |
mainSlide[3] | Pink |
次に、サムネイルをクリックしたときに、スライダーの画像内でひサムネイルのvalueのカラー名と、2で変数に格納したカラー名と一致する画像を表示する
これを実現するためには、
サムネイルのvalue(カラー名が入っている)を取得し、1で取得したalt情報と一致しているとき、その配列が何番目かを返すようにしたい
やりたいことは明快。あとはそれを実現できる関数があるか、ですが、
参考 val()関数
参考 indexOfメソッド
ありました。これを利用します。
val()でサムネイルのvalue(カラー名が入っている)を取得し、1で取得したalt情報と一致しているとき、その配列indexを返すようにします。
indexOfメソッドを使うと、引数に指定した値が配列に存在していたら、何番目の要素かを表す番号を返します。
存在していなければ、-1を返します。
以下のスクリプトを追記します。
<script>
$('.variant-swatches').on( 'click', 'input', function() {
var pickColor = $(this).val();
console.log(pickColor);
console.log(mainSlide[0]);
var changeSlideColor = mainSlide.indexOf(pickColor);
console.log(changeSlideColor);
$carousel.flickity( 'select', changeSlideColor );
});
</script>
これで以下のスライダーのAPI
を叩くための、
スライダー画像のindex番号をカラー名から逆引きして指定できるようになりました!
コアjsを作り込まず実現できたことでコード管理はかなりシンプルになりました。
このあとまた新たな特性のバリアントが出てきて、それに見合った表示をしたい要件が出てきても、詰将棋のような閃きがあれば、柔軟に対応できるかと存じます。