Kestrel の FAQ ページは長くなりすぎて、お客様が探している答えを通り過ぎてしまうほどでした。マーチャントは、上部に検索ボックスがある競合のヘルプページを見せ、同じものを求めました。単語を入力すると、一致する質問だけが表示されるものです。
これはその Shopify FAQ 検索の話です。snippet、最初の版が本番ストアで何も見つけられなかったバグ、そして Shopify テーマのインラインスクリプトについての教訓になった修正を紹介します。
この事例について:実際の Shopify プロジェクトをもとに再構成したものです。ブランド名と一部の詳細は変更しています。コード、バグ、修正は実際に起きたとおりです。

Shopify 標準の検索を使わない理由
Shopify のストア検索は、商品、コレクション、ページ、ブログ記事を対象にしています。マーチャントがテーマのセクションブロックに入力したテキスト、つまり多くの FAQ セクションが質問を保存している場所は、その検索対象に含まれません。ストアで「retour」と検索したお客様が見つけるのは商品で、返品についての答えではありません。
しかし FAQ の答えはすでにページの HTML の中にあります。ブラウザ上で絞り込めば、リクエストもアプリもインデックスも不要です。お客様が入力している途中から結果が表示されます。
最初の版:snippet ひとつと render 1行
マーチャントはコードエディタでテーマを編集するため、変更はできるだけ小さくしました。マークアップ、スタイル、スクリプトを含む新しい snippet ひとつと、それを表示する FAQ セクション内の1行です。
{%- render 'faq-search', section: section -%}
<div class="faqs ..."> <!-- the theme's existing FAQ markup -->
スクリプトは小さなカスタム要素です。入力のたびに4つのことを行います。
- テキストを正規化します。小文字にしてアクセント記号を外すので、「e」で「é」も見つかります。
- 検索語を単語に分け、タイトルか答えにすべての単語を含む質問だけを残します。
- 一致する質問がひとつもないカテゴリーは、見出しごと非表示にします。
- まったく一致しない場合は「質問が見つかりませんでした」と表示し、サポートへの問い合わせを促します。
ヘッドレスブラウザで FAQ マークアップのコピーを使ってテストしました。どの検索でも正しい質問が返りました。そこで納品しました。
届いたスクリーンショット
返信にはスクリーンショットが付いていました。マーチャントは答えの一文をまるごと検索ボックスに貼り付けていました。ページには「質問が見つかりませんでした」と表示され、その下にはすべての質問がそのまま見えていました。何も隠れず、何も見つかっていません。
2つの症状は同じことを示していました。スクリプトはページに質問がないと思い込んでいたのです。本番ページを開いて要素に直接問い合わせると、ページには FAQ のグループが2つ、質問が7つありました。検索要素が持つグループのリストは空でした。
原因:早すぎるタイミングで動くインラインスクリプト
snippet は質問の上に表示され、その <script> タグは snippet の中にあります。ブラウザは HTML を上から順に解析します。スクリプトに到達するとカスタム要素が定義され、<faq-search> はすでにドキュメント内にあるため、その connectedCallback がすぐに実行されます。この時点では、パーサーはまだ下にある質問まで読み進めていません。
最初の版は connectedCallback で FAQ のグループを探して保存していました。保存されたのは空のリストで、それが一度きり、ずっと使われ続けました。テストが通ったのは、テストページでたまたまスクリプトが質問の後ろにあったからです。

修正は getter ひとつです。要素の接続時にグループを保存する代わりに、お客様が入力するたびにスクリプトがグループを探します。
get groups() {
return Array.from(this.closest('.section').querySelectorAll('.faq'));
}
誰かが入力を始める頃には、ページはすでにすべて解析されています。数十件の質問なら探す処理はほぼ負荷がかからず、マーチャントの編集中にテーマエディタがセクションを再描画しても動き続けます。キャッシュしたリストではそうはいきません。
もうひとつの目立たないバグ
コードを最終的な snippet に移す途中で、もうひとつ問題が見つかりました。絞り込んだ項目を隠す CSS ルールが .faq [data-faq-hidden] と、スペース入りで書かれていたのです。これではグループ内の質問は隠れても、グループ自体は隠れず、下に何もないカテゴリー見出しが残ることがありました。現在のルールは両方を対象にしています:.faq [data-faq-hidden], .faq[data-faq-hidden]。

実際に動く場所でテストする
今回は本番の FAQ ページそのもので、修正したロジックを実際のマークアップに対してブラウザ上で実行してテストしてから、マーチャントに変更をお願いしました。
| 「levertijd」 | 配送日数の質問だけ |
| 「garantie」 | 2つの異なるカテゴリーにある保証の質問 |
| 「€70 gratis」 | 送料の質問だけ(答えの文で一致) |
| 「retour」 | 返品の質問だけ。もう一方のカテゴリーは非表示 |
| 「xyz」 | 該当なし。「質問が見つかりませんでした」と表示 |
学んだこと
- 本番ページと同じ順序でテストしましょう。インラインスクリプトで定義したカスタム要素には、自分より上の HTML しか見えません。
- コンテンツがすでに存在すると分かっている場合を除き、
connectedCallbackで DOM の検索結果をキャッシュしないでください。getter はDOMContentLoadedを待つより簡単で、テーマエディタの再描画にも耐えます。 - マーチャントからスクリーンショットが届いたら、症状を組み合わせて読みましょう。「結果なし」と「何も隠れていない」の組み合わせで、コンソールを開く前に原因を絞り込めました。
この検索はその後、本格的なヘルプページの一部になりました。詳しくはShopify FAQ ページテンプレートの事例をご覧ください。同じストア向けに作った商品ページのオファーは、1つ買うと1つ無料の選択機能で紹介しています。FAQ やテーマのほかの部分でこうした作業が必要な場合は、Shopify テーマ開発サービスをご覧ください。
よくある質問
FAQ の検索バーは SEO に悪影響がありますか?
いいえ。すべての質問と答えは HTML に残ります。お客様が検索語を入力した後、そのブラウザ上で非表示になるだけです。
Shopify の FAQ ページに検索を付けるのにアプリは必要ですか?
いいえ。質問はすでにページ上にあるので、小さなスクリプトで絞り込めます。アプリより速く、月額費用もかかりません。
FAQ が多くても使えますか?
はい。数百件の質問ならブラウザ上で瞬時に絞り込めます。それ以上なら、FAQ を複数のページに分けるほうがお客様にとって分かりやすいことが多いです。
答えの中も検索しますか?
はい。各質問はタイトルと答えの文章でアクセント記号を無視して照合され、検索語のすべての単語が含まれている必要があります。