ShopifyのDawnテーマのメモ

Shopify

調べたことなどメモ的に。まだ調査中のため適宜内容を書き換えているのと、現状では根拠となる公式のドキュメントがないのとで、正確性は保証できないためその点留意ください。

前提

  • ShopifyのDawnテーマのv1.0.0が対象

初期バージョンの話なので、今後変更される可能性が常にありますから、本当に現時点でのメモ程度になります。

調べたこと

CSS

  • セクションごとにlinkタグで外部CSSを読み込ませている
  • いくつかはmedia="print"の遅延読み込みを実装している

サンプルとしてsection/collection-list.liquidの上部のコードを記載します。


{{ 'section-collection-list.css' | asset_url | stylesheet_tag }}

<link rel="stylesheet" href="{{ 'component-slider.css' | asset_url }}" media="print" onload="this.media='all'">
<noscript>{{ 'component-slider.css' | asset_url | stylesheet_tag }}</noscript>

PageSpeed InsightsやGoogle Lighthouseの点数は高いので、ページ内にlinkタグによるCSSの読み込みがあっても速度に顕著な悪影響はなさそうでした。
Chromeの開発者ツールのPerformanceでも測定しましたが、Timelineを見る限り特に問題があるようにも見えず。

Shopifyに限らずwebサイト作成全般として、この手法は調査研究した方が良いかもしれません。

動的ソース

上記が該当しますが、メタフィールドと動的ソースを用いれば以下のようなことが可能になるはずです。

  • 商品ページの下部に、商品説明部とは別に商品に関するサイズなどの情報を表示する機能と枠を作成できる
  • コードを編集せずに、枠を増やしたり対象とする内容を変更できる(サイズを入れたメタフィールドから、原材料を入れたメタフィールドに変更できるなど)

2.0より前ではメタフィールドの出力部分をliquidファイルに直書きしていましたが、2.0以降では出力をカスタマイズで制御できるようになるためノーコードの側面が強くなります。

cart.attributes(カートページ内の入力欄など)

本来はcheckoutsection/main-cart-items.liquidにあるformタグの中に書けば良いはずです。

しかし、例えばformタグの中にselectタグでドロップダウンを表示させ、項目を変更しますと以下のようなエラーがチェックアウトボタンのしたあたりに表示されます。


//言語が英語なら以下
There was an error while updating your cart. Please try again
//言語が日本語なら以下
カートをアップデートするときにエラーが発生しました。もう一度お試しください。

原因はおそらく以下の通り。

  1. formタグを包含する形でカスタムタグのcart-itemsが記述されている
  2. cart.jsにてcart-itemsの中が監視されている
  3. ドロップダウンで内容を変更するとchangeイベントが発火する
  4. 本来カート内で想定している「商品の削除」「商品点数の増減」という動作以外、つまり追加したselectタグを操作するとエラーになる(と思う)

そのため簡易な対策として、カスタムタグのcart-itemsの外側にselectタグを書き、そこにform属性を追加することで対応する方法が検討できます。


<select id="" name="attributes[timeSelector]" form="cart">
  <option value="時間指定なし">時間指定なし</option>
  <option value="時間指定あり">時間指定あり</option>
</select>

form外のsubmitボタンを動作させる件は以下ページなどを参照のこと。

ただしinputタグのform属性はIE未対応のようなので、このままではIE切り捨てになってしまいます。

そこで、同じ構造を持つチェックアウトボタンに使われているIE用のJSを流用する方法も検討しました。

IE対応のためのJS

section/main-cart-footer.liquid下部にあるJSで動作を行うようです。


<script>
  document.addEventListener('DOMContentLoaded', function() {
    function isIE() {
      const ua = window.navigator.userAgent;
      const msie = ua.indexOf('MSIE ');
      const trident = ua.indexOf('Trident/');
      return (msie > 0 || trident > 0);
    }
    if (!isIE()) return;
    const cartSubmitInput = document.createElement('input');
    cartSubmitInput.setAttribute('name', 'checkout');
    cartSubmitInput.setAttribute('type', 'hidden');
    document.querySelector('#cart').appendChild(cartSubmitInput);
    document.querySelector('#checkout').addEventListener('click', function(event) {
      document.querySelector('#cart').submit();
    });
  });
</script>

カート用のformタグの中にinputタグを入れている必要性が理解できていませんが(おそらくname="checkout"がないとエラーになる)、チェックアウトボタンのクリックを検知してJSでsubmitを実行しており、これを改造して用います。

まずselectタグにidをつけます。


<select id="timeSelector" name="attributes[timeSelector]" form="cart">
  <option value="時間指定なし">時間指定なし</option>
  <option value="時間指定あり">時間指定あり</option>
</select>

続いて前述の以下のような記述を追加して書き換えます。


<script>
  document.addEventListener('DOMContentLoaded', function() {
    function isIE() {
      const ua = window.navigator.userAgent;
      const msie = ua.indexOf('MSIE ');
      const trident = ua.indexOf('Trident/');

      return (msie > 0 || trident > 0);
    }

    if (!isIE()) return;
    const cartSubmitInput = document.createElement('input');
    cartSubmitInput.setAttribute('name', 'checkout');
    cartSubmitInput.setAttribute('type', 'hidden');
    document.querySelector('#cart').appendChild(cartSubmitInput);
    document.querySelector('#checkout').addEventListener('click', function(event) {
      //追加ここから
      const timeSelector =  document.querySelector('#timeSelector');
      const timeSelectorInput = document.createElement('input');
      timeSelectorInput.setAttribute('name', 'attributes[timeSelector]' );
      timeSelectorInput.setAttribute('type', 'hidden');
      timeSelectorInput.value = timeSelector.value;
      document.querySelector('#cart').appendChild(timeSelectorInput);
      //追加ここまで
      document.querySelector('#cart').submit();
    });
  });
</script>

流れは以下の通り。

  1. チェックアウトボタンのclickを検知
  2. selectタグのvalueを取得
  3. inputタグを生成してselectタグのvalueを入れ、カート用のformの中に入れる
  4. submitで送信

実際に動作は確認していませんが、おそらく動くかなと思います。

なお、別の候補としてはformDataを使う方法も考えてはいました。上記がダメなら検討できるかもしれません。

商品詳細ページのform

商品価格など出力するmain-product.liquidには以下2種のformタグが存在します。

  • 分割払いに関連した条件に関するフォーム(※後ほど記述)
  • カートに入れるボタン用のフォーム

以前のようにformタグで全体を内包する形ではなくなっています。

仮にこれらのフォームの中にtype="text"inputタグを書いた場合、このテキストエリアにフォーカスが当たっている状態でEnterを押すと以下のような動作になります。

  • 分割払い用フォームは、即時にチェックアウトページに移動(動的チェックアウトと同様の動作)
  • カートに入れるボタン用のフォームは、即時にカートに追加(カートに追加ボタンを押した場合と同様の動作)

分割払いに関連したフォーム

現在は早期プログラムとして一部のストアでしか使えない、分割払い機能のための購入ボタンが表示されるようです。

今後は動的チェックアウトボタンと同様に、分割払い用ボタンも各テーマの標準セットになっていくのではと思います。

properties(名入れやカレンダーなど)

前出のcart.attributesと同様に、form属性をつけることで対応可能です。
試したところ、type="color"によるカラーコードにも対応していたので、通常のform同様にinputタグのtypeはどれを設定してもOKだと思います。

なお設置場所的にテーマエディタで扱えるセクションでの実装可能性が高いと思いますので、その場合は以下の記事が参考になります。

ただし動的チェックアウトで問題が起こる可能性があります。この件含めて以下記事にまとめました。

以前に調べたカスタマイザーを経由しない別の方法に関しては以下を参照ください。
ただし現在(V5.0.0以降)ではJSの記述が変更されているため、以下の方法での実現は難しいと思います。

カスタマイザーを経由しない古いバージョン用のサンプルコードを見る
  • formタグの中に書けば動くが、意図しない動作になる
  • formタグの外に書くしかないが、form属性をつけただけでは動かない
  • JSを使って登録の準備を行う必要がある

ざっくりまとめると上記のようになります。この件に関連して、以下のスレッドに対応策となるサンプルコードが紹介されています。

手順としては以下のようになります。

  1. section/main-product.liquid{%- form 'product', 省略 -%}の中に、以前と同じようにname=properties[hoge]と記述
  2. assets/pruduct-form.jsconfig.body = JSON.stringify({(以前はconst body = JSON.stringify({)の中に設定したproperties[hoge]の値を設定する。

前出のサンプルを少し手直して流用するとassets/pruduct-form.jsは以下のようになります(なぜかjQueryを使う前提のコードになっていましたのでそこだけ直しています)。


config.body = JSON.stringify({
  ...JSON.parse(serializeForm(this.form)),
  sections: this.cartNotification.getSectionsToRender().map((section) => section.id),
  sections_url: window.location.pathname,
});

//上記を以下のように変更

config.body = JSON.stringify({
  ...JSON.parse(serializeForm(this.form)),
  sections: this.cartNotification.getSectionsToRender().map((section) => section.id),
  sections_url: window.location.pathname,
    properties: { Pinfel: document.querySelector('#pinfel-select select').value }
});

スレッドはさらに続いていてこのコードでは問題がありそうな内容も目にしましたが、その点時間が取れずまだ未確認です。
Dawnのバージョンアップでまた変わるかもしれませんし、流動的な部分として考えておいた方が良さそうです。

結び

主に製作外の方からの速度面での評価が高いDawnですが、以前のテーマと違う部分は結構あるようなので、場合によってはいろいろ大変になるかもしれません。

確かめていませんがアプリとの相性も気になるところです。

7人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント欄