こんにちは、Webディレクターの BigVi です。最近暖かくなりましたね。週末はプールに泳いだら気持ちよかったです。
前に、お客さまから「FAQページに飛んでも、答えが見つからない」と言われました。FAQが30件以上あって、全部閉じた状態です。お客さまのサポートチームも「問い合わせがまだ多い」と困っていたみたいです。
私もよくあります。ヘルプのリンクを押したのに、Qがたくさん並んでいて「どれ?」ってなります。面倒で、もうチャットに聞いちゃいます。。。
今回はこの問題を直しました。リンクからFAQに飛んだとき、そのQAまで自動スクロールして、1つだけパッと開きます。他は閉じたまま。これだけですごく良くなりました。
やりたいこと
やることはシンプルです。
- サイト内リンクから
#faq-xxx付きのURLでFAQに飛ぶ - そのQAまでスムーススクロールする
- そのQAだけ、300msで開く
- 他は閉じたまま
JSはURLの#のあとを読むだけ。とても軽いです。
HTML構造
FAQのHTMLです。<details>と<summary>を使います。これはブラウザの標準機能で、JSなしでも開閉できます。便利です。
<div class="faq-list">
<details class="faq-item" id="faq-cancel">
<summary class="faq-question">
キャンセルはいつまでできますか?
</summary>
<div class="faq-answer">
<p>ご利用開始日の3営業日前まで、
マイページからキャンセルできます。</p>
</div>
</details>
<details class="faq-item" id="faq-payment">
<summary class="faq-question">
支払い方法は何がありますか?
</summary>
<div class="faq-answer">
<p>クレジットカードと銀行振込が使えます。</p>
</div>
</details>
<details class="faq-item" id="faq-trial">
<summary class="faq-question">
無料トライアルはありますか?
</summary>
<div class="faq-answer">
<p>はい、14日間の無料トライアルがあります。</p>
</div>
</details>
</div>大事なのは、各<details>にidを付けることです。id="faq-cancel"みたいに。URLの#でこのIDを指します。これを忘れると動かないです。
CSSスタイル
開閉のアニメーションと、ターゲットのハイライトを作ります。
.faq-list {
max-width: 720px;
margin: 0 auto;
}
.faq-item {
border-bottom: 1px solid #e5e7eb;
overflow: hidden;
}
.faq-question {
padding: 20px 16px;
font-weight: 600;
cursor: pointer;
list-style: none;
display: flex;
justify-content: space-between;
align-items: center;
}
/* デフォルトの▶を消す */
.faq-question::-webkit-details-marker {
display: none;
}
/* 開閉アイコン */
.faq-question::after {
content: "+";
font-size: 1.25rem;
color: #6b7280;
}
.faq-item[open] .faq-question::after {
content: "−";
}
/* 回答のアニメーション */
.faq-answer {
padding: 0 16px 20px;
color: #4b5563;
line-height: 1.8;
animation: slide-down 0.3s ease forwards;
}
@keyframes slide-down {
from {
opacity: 0;
transform: translateY(-8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ターゲットのハイライト */
.faq-item.is-target {
background-color: #eff6ff;
border-left: 3px solid #3b82f6;
transition: background-color 2s ease;
}
.faq-item.is-target.fade-out {
background-color: transparent;
border-left-color: transparent;
}
/* アニメーション苦手な人向け */
@media (prefers-reduced-motion: reduce) {
.faq-answer {
animation: none;
}
}is-targetクラスで、リンクから飛んできたQAに薄い青い背景を付けます。「ここですよ」って教える感じ。2秒後にゆっくり消えます。邪魔にならないし、でもちゃんと分かります。
JavaScriptの実装
メインのJSです。やることは3つだけ。
- URLの#のあとを読む
- そのFAQまでスクロールする
- 開いて、ハイライトを付ける
document.addEventListener('DOMContentLoaded', function() {
var hash = window.location.hash;
if (!hash) return;
var targetFaq = document.querySelector(hash);
if (!targetFaq) return;
if (!targetFaq.classList.contains('faq-item')) return;
// 1. 他のFAQを全部閉じる
document.querySelectorAll('.faq-item[open]').forEach(function(item) {
if (item !== targetFaq) {
item.removeAttribute('open');
}
});
// 2. ターゲットを開く
targetFaq.setAttribute('open', '');
// 3. スクロール(ヘッダーの分だけ上に余白)
var offset = 80;
var top = targetFaq.getBoundingClientRect().top
+ window.scrollY - offset;
window.scrollTo({
top: top,
behavior: 'smooth'
});
// 4. ハイライトを付ける
targetFaq.classList.add('is-target');
// 3秒後にフェードアウト
setTimeout(function() {
targetFaq.classList.add('fade-out');
}, 3000);
// 5秒後にクラスを消す
setTimeout(function() {
targetFaq.classList.remove('is-target', 'fade-out');
}, 5000);
});コード短いですよね。外部ライブラリは使ってないです。window.location.hashでURLの#のあとを取って、querySelectorで探すだけ。
offsetの80は、固定ヘッダーの高さの分です。ヘッダーがないサイトは0でいいです。私は最初これを忘れて、ターゲットがヘッダーの下に隠れました。リーダーに「見えないよ」って言われて気づきました。。。
リンクの書き方
他のページからFAQの質問をリンクするときは、こう書きます。
<!-- 料金ページに -->
<p>
お支払い方法は
<a href="/faq/#faq-payment">こちらのFAQ</a>
をご覧ください。
</p>
<!-- サポートページに -->
<p>
キャンセルについては
<a href="/faq/#faq-cancel">こちら</a>
をご確認ください。
</p>これだけです。リンクを押したら、FAQページに飛んで、自動で開いてくれます。
ページ内リンクも対応する
同じFAQページの中で「関連するQ」にリンクしたいときもあります。同じページ内だと、DOMContentLoadedは動かないです。だからhashchangeも使います。
function openTargetFaq() {
var hash = window.location.hash;
if (!hash) return;
var targetFaq = document.querySelector(hash);
if (!targetFaq) return;
if (!targetFaq.classList.contains('faq-item')) return;
// 前のハイライトを消す
document.querySelectorAll('.faq-item.is-target')
.forEach(function(item) {
item.classList.remove('is-target', 'fade-out');
});
// 他を閉じて、ターゲットを開く
document.querySelectorAll('.faq-item[open]')
.forEach(function(item) {
if (item !== targetFaq) item.removeAttribute('open');
});
targetFaq.setAttribute('open', '');
// スクロール
var offset = 80;
var top = targetFaq.getBoundingClientRect().top
+ window.scrollY - offset;
window.scrollTo({ top: top, behavior: 'smooth' });
// ハイライト
targetFaq.classList.add('is-target');
setTimeout(function() {
targetFaq.classList.add('fade-out');
}, 3000);
setTimeout(function() {
targetFaq.classList.remove('is-target', 'fade-out');
}, 5000);
}
// ページを開いたとき
document.addEventListener('DOMContentLoaded', openTargetFaq);
// ページ内リンクを押したとき
window.addEventListener('hashchange', openTargetFaq);関数にまとめました。どこでリンクしても同じ動きになってくれます。
実際の効果
お客さまのサイトに入れました。FAQは32件です。サポートページや料金ページから、それぞれのQAにリンクを付けました。
1ヶ月後にA/Bテストの結果が出ました。
| 指標 | 入れる前 | 入れたあと |
|---|---|---|
| FAQ経由のサポート問い合わせ率 | 12.3% | 7.8%(約37%減) |
| FAQの平均滞在時間 | 18秒 | 42秒 |
| FAQ→次ページへの遷移率 | 23% | 41% |
サポートの問い合わせが減ったのが一番うれしかったです。お客さまも「FAQがやっと使えるようになった」と喜んでくれました。
滞在時間が伸びたのは、ちゃんと回答を読んでくれているということ。前は「見つからない→離脱」だったのが、「見つかった→読む→次のページ」になりました。いい感じです。
気をつけること
IDの名前を揃える
FAQが増えると、IDがバラバラになります。faq-を最初に付けるルールを決めてください。私のプロジェクトではCMSのスラッグと合わせています。後から直すのは大変です。
ヘッダーにかぶる
固定ヘッダーがあると、スクロール先はヘッダーの下で隠れます。さっき言ったoffsetの値を変えてください。レスポンシブでヘッダーの高さが変わるときは、JSで高さを取るといいです。
スクリーンリーダー
<details>/<summary>はスクリーンリーダーでも使えます。でも、自動で開いたあとにフォーカスは回答に動かすともっと親切です。
// フォーカスを回答に動かす
var answer = targetFaq.querySelector('.faq-answer');
answer.setAttribute('tabindex', '-1');
setTimeout(function() {
answer.focus();
}, 500);500msはスクロールが終わるのを待つ分です。
まとめ
今回は、FAQで「答えが見つからない」問題を直す方法を書きました。URLの#を使って、自動スクロール&展開します。
ポイントは3つです。
<details>/<summary>を使えば、開閉はJSなしで動く- URLの#を読むだけで、自動スクロール+展開ができる
- ハイライトは一時的に付けて消す。「ここですよ」って伝えるだけ
このプロジェクトでは、サポートの問い合わせが約37%減りました。FAQが「置いてあるだけ」から「ちゃんと使えるページ」に変わっただけです。コードも50行ぐらいで、コストが低いです。やったほうがいいと思います。
株式会社ファストコーディングでは、こういうUI改善の実装サポートをしています。FAQの改善やサイトのUXを良くしたい方は、こちらのお問い合わせフォームから連絡してください。
※本記事は弊社外国人スタッフによる投稿です。言い回しや表現が不十分な個所がありますことご容赦いただきますようお願いいたします。
株式会社ファストコーディング

