Shopifyの日本語設定Themeでサジェストを出す方法


Shopifyの日本語設定Themeでサジェストを出す方法

Dawnテーマで、検索をしてみると下にグルグルが出たままで何もサジェスト出てこない。
ってなってませんか?
これは、テーマの言語設定が日本語になっていると使えません。
https://shopify.dev/api/ajax/reference/predictive-search#requirements-and-limitations

ですが、諦めてはいけません。
エンジニア向けになりますが、コードを組み込めばサジェストを出せるようになります。

サジェストを出すためのコードと手順

参考はこちらです
https://gist.github.com/atikju/0c0f3c52174f6e75809d6a86490a7da1

これをもとに使いやすいように少しカスタムしています。(jQuery必須です)

search.json.liquidをテンプレートに追加

search.json.liquidをtemplatesフォルダ内に作成し、以下のコードを追加します。

{% comment %}
このファイルをテンプレートに作成する必要があります。
検索用のテンプレートを作成してJSONに名前を付けます。
最後のファイル名はsearch.json.liquidです
{% endcomment %}

{% layout none %}
{% comment %}
Inspired by: http://ecommerce.shopify.com/c/ecommerce-design/t/diy-implementing-autocomplete-with-search-144104
 {% endcomment %}

{%- comment -%}
 ページネーションの値を変更することでサジェストに表示できるアイテム数を変更できます。
 resultオブジェクトから出力する値を修正すると、表示できる値が変わります。
{%- endcomment -%}
{% paginate search.results by 10 %}
 {% capture output %}
 {% for result in search.results %}
        {% assign resultURL = result.url %}
        {% assign thumbURL = result.images[0] | img_url: 'master' %}

        {"title":"{{ result.title | replace: '\\', '\\\\' | replace: '"', '\\"' | replace:  '/','\\/' }}",
           "url":"{{ result.url   | replace: '\\', '\\\\' | replace: '"', '\\"' | replace:  '/','\\/' }}",
         "thumb":"{{ thumbURL     | replace: '\\', '\\\\' | replace: '"', '\\"' | replace:  '/','\\/' }}",
            "id":{{result.id}} }{% unless forloop.last %},{% endunless %}
    {% endfor %}
{% endcapture %}

{% comment %} Output the json object {% endcomment %}
{"results_total":{{paginate.items}},"results":[{{ output | strip_newlines }}]}

{% endpaginate %}

任意の場所に、コードを追加するかsnippetを作成して検索フォームを入れる

以下のコードを検索フォームを表示したい場所に追加するか、使い回したい場合はsnippetを作成し読み込む。
特定のフォームを作成済みで、jsを読んで対象のセレクターなどに変更など、カスタマイズできる人は次の工程に進んでください。

{% comment %}
このファイルはスニペットとして追加することができ、表示したい場所に追加することができます。
{% endcomment %}

<div id="pageheader">
  <div class="util-area">
      <div class="search-box">
        <form class="search-form" action="/search" method="get" _lpchecked="1">
            <i class="icon-mag"></i>
            <input type="text" name="q" placeholder="Search" autocomplete="off">
            <input type="submit" value="検索する">
        </form>
        <div class="results-box"></div>
      </div>
    </div><!-- /.util-area -->
</div><!-- /#pageheader -->

ajax-search-script.jsを作成し、Theme.liquidに読み込む

ajax-search-script.jsを作成し、Theme.liquidの</body>のなるべく近いところで読み込みます。
サジェストを出したいフォームが特定のページにしかないのであれば、特定のページのコードの最後の方で読み込んでもOKです。

/*
このスクリプトにはjQueryライブラリが必要です。
ボディタグがTheme.Liquidで終了する直前にこのファイルを追加してください。
*/

$(document).ready(function(){
  /// メイン検索入力
  $('#pageheader .search-box input[type="text"]').bind('focusin focusout', function(e){
      $(this).closest('.search-box').toggleClass('focus', e.type == 'focusin');
  });


  /// ライブ検索
  let searchTimeoutThrottle = 500;
  let searchTimeoutID = -1;
  let currReqObj = null;
  let resultsBox = $('.results-box');

  $('#pageheader .search-box input[type="text"]').bind('keyup change', function(){

    // 検索の文字列が2文字より多くて、前回のワードと違う時
    if($(this).val().length > 2 && $(this).val() != $(this).data('oldval')) {
          //前の値をリセットします
          $(this).data('oldval', $(this).val());

          // 通信中のAjaxリクエストを中断
          if(currReqObj != null) currReqObj.abort();

          // 以前の検索を中断
          clearTimeout(searchTimeoutID);

          let searchForm = $(this).closest('form');

          //検索キーワード
          let term = searchForm.find('input[name="q"]').val();

          //検索ページへのURL
          let linkURL = searchForm.attr('action') + '?type=product&q=' + term;

          searchTimeoutID = setTimeout(function(){
              currReqObj = $.ajax({
                url: searchForm.attr('action'),
                data: {
                  type: 'product',
                  view: 'json',
                  q: term,
                },
                dataType: "json",
                success: function(data){
                  currReqObj = null;
                  if(data.results_total == 0) {
                      // 一致する商品がない時
                      resultsBox.html('<div class="note">'+ '一致する商品はありません。' +'</div>');
                  } else {
                    // 一致する商品がある時
                    resultsBox.empty();
                      $.each(data.results, function(index, item){
                        let $row = $('<a></a>').attr('href', item.url);
                        $row.append('<div class="img"><img src="' + item.thumb + '" /></div>');
                        $row.append('<div class="d-title">'+item.title+'</div>');
                        resultsBox.append($row);
                      });

                      // 全ての結果を見るボタン
                      resultsBox.append('<a href="' + linkURL + '" class="note">対象の商品を見る</a>');
                  }
                }
              });
          }, searchTimeoutThrottle);

      } else if ($(this).val().length <= 2) {
          //キーワードが2以下の時にサジェストの内容を削除する
          resultsBox.empty();
      }
  }).attr('autocomplete', 'off').data('oldval', '').bind('focusin', function(){
      // 再フォーカスされた時にサジェストを表示します
      resultsBox.fadeIn(200);
  }).bind('click', function(e){
      //クリックして、ボディが[イベント]をクリックしないようにします
      e.stopPropagation();
  });

  //サジェスト以外をクリックした時に、サジェストを非表示にする
  $('body').bind('click', function(){
      resultsBox.fadeOut(200);
  });


  //検索するボタンをクリックした時の挙動
  $('.search-form, #search-form').on('submit', function(e){
    e.preventDefault();
    let term = $(this).find('input[name="q"]').val();
    let linkURL = $(this).attr('action') + '?type=product&q=' + term;
    window.location = linkURL;
  });

});

全体の仕組み

全体の仕組みとしては、Shopifyのサジェスト用のAPIへデータを直接取りに行っているのではなく、
検索フォームに入力されたキーワードで検索をした結果を、非同期で取得しそれを表示しています。

jsの内容が、オリジナルのものと少し違いますが実際にそのままで組み込んだ場合にエラーになってしまう部分を削除したり変更したりしています。

まとめ

拡張子を.json.liquidにして、APIでviewをjsonにしてあげると、ページデータをjsonで取得できることに感動しました。
 
Dawnのサジェストが日本語では、表示できず諦めそうになっていた人はこちらで試されてみてください。