UI/UX
投稿日:

5秒の無操作で「次のおすすめ」を静かに出す ─ 迷いを拾うフェードイン演出

こんにちは、株式会社ファストコーディングのBigViです。昨日、会社の昼休みにスマホでヘッドフォンを探していました。商品ページを開いて、スクロールして、レビューを読んで……そこで止まりました。「いいけど、他にもっといいのがあるかも」って。。でもトップに戻るのも面倒で、5秒ぐらいそのまま固まっていました。

先日、お客さまから「商品ページの滞在時間は長いのに、CVRが低い」と相談されました。ユーザーはちゃんと読んでいる。でも、読み終わった後の行動がない。ヒートマップを見ると、ページの下のほうで動きが止まる人が多いみたいです。

私もよくあります。ページを読み終えて「で、どうしよう?」って迷う瞬間。。

今回は「5秒間の無操作を検知したら、右下に『次のおすすめ』を静かにフェードインさせる」という実装をしました。ポップアップではなく、小さなカードが静かに現れるだけです。

なぜ「5秒」なのか

「迷い」を検知するのは難しいです。ユーザーが本当に迷っているのか、ただじっくり読んでいるのか、判断できません。

でも、Webページでの無操作時間には傾向があります。スクロール、クリック、マウス移動のどれもが5秒以上ない場合、多くのユーザーは「次の行動を考えている」か「興味を失いかけている」状態です。

3秒だと早すぎます。文章を読んでいる最中に出てしまいます。10秒だと遅すぎて、すでに離脱している可能性が高い。5秒は「読み終わって、ちょっと考えている」タイミングです。

ある案件で最初は3秒にしていました。クライアントに見せたら「読んでる途中に出てきてびっくりした」と言われました。。5秒にしたら「あ、ちょうどいいタイミングで出てくる」と言ってくれました。

ただし、5秒は1つの目安です。コンテンツの種類やユーザー層によって、最適な時間は変わります。

実装方法

HTML構造

「次のおすすめ」カードは、ページの最後に置きます。

<div class="next-action" id="js-next-action" role="complementary" aria-label="おすすめコンテンツ">
  <div class="next-action-inner">
    <p class="next-action-label">次のおすすめ</p>
    <a href="/products/sofa-b" class="next-action-link">
      <img src="/images/sofa-b-thumb.jpg" alt="ソファB" class="next-action-img" width="60" height="60" loading="lazy" />
      <span class="next-action-text">
        <span class="next-action-title">2人掛けコンパクトソファ</span>
        <span class="next-action-price">¥34,800</span>
      </span>
    </a>
    <button class="next-action-close" id="js-next-action-close" aria-label="閉じる">×</button>
  </div>
</div>

role="complementary"をつけて、補足的なコンテンツであることを伝えます。メインコンテンツを邪魔しない存在です。

CSSスタイル

右下に固定で、フェードインします。控えめなデザインにしています。

.next-action {
  position: fixed;
  bottom: 24px;
  right: 24px;
  background: #fff;
  border: 1px solid #e0e0e0;
  border-radius: 10px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  padding: 14px;
  max-width: 280px;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.5s ease, transform 0.5s ease;
  pointer-events: none;
  z-index: 100;
}

.next-action.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.next-action-label {
  font-size: 11px;
  color: #888;
  margin: 0 0 8px;
  font-weight: 500;
}

.next-action-link {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: #333;
}

.next-action-img {
  border-radius: 6px;
  object-fit: cover;
  flex-shrink: 0;
}

.next-action-title {
  display: block;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.4;
}

.next-action-price {
  display: block;
  font-size: 14px;
  color: #1a73e8;
  font-weight: 700;
  margin-top: 2px;
}

.next-action-close {
  position: absolute;
  top: 8px;
  right: 8px;
  background: none;
  border: none;
  font-size: 14px;
  color: #bbb;
  cursor: pointer;
  padding: 2px 6px;
}

@media (prefers-reduced-motion: reduce) {
  .next-action {
    transition: opacity 0.01s;
    transform: none;
  }
}

@media (max-width: 480px) {
  .next-action {
    bottom: 16px;
    right: 16px;
    left: 16px;
    max-width: none;
  }
}

フェードインは0.5秒かけています。「急に現れた」感じを出さないためです。影も薄くして、ページに溶け込むようにしました。

JavaScriptの実装

マウス移動、スクロール、クリック、タッチ、キーボードの5つのイベントを監視して、5秒の無操作を検知します。

document.addEventListener('DOMContentLoaded', () => {
  const nextAction = document.getElementById('js-next-action');
  const closeBtn = document.getElementById('js-next-action-close');
  if (!nextAction) return;

  let timer = null;
  let hasShown = false;
  const IDLE_MS = 5000;

  const events = ['mousemove', 'scroll', 'click', 'touchstart', 'keydown'];

  function resetTimer() {
    if (hasShown) return;
    clearTimeout(timer);
    timer = setTimeout(showNextAction, IDLE_MS);
  }

  function showNextAction() {
    if (hasShown) return;
    hasShown = true;

    nextAction.classList.add('is-visible');

    // イベントリスナーを解除
    events.forEach((e) => {
      document.removeEventListener(e, resetTimer, { passive: true });
    });
  }

  // 閉じるボタン
  if (closeBtn) {
    closeBtn.addEventListener('click', () => {
      nextAction.classList.remove('is-visible');
    });
  }

  // 監視開始
  events.forEach((e) => {
    document.addEventListener(e, resetTimer, { passive: true });
  });

  // 初回タイマー開始
  timer = setTimeout(showNextAction, IDLE_MS);
});

ポイントは3つです。

1つ目はhasShownフラグです。一度表示したら、もう出さないようにしています。閉じた後にまた5秒待って出てきたら、うるさいです。

2つ目はpassive: trueです。スクロールイベントのリスナーにpassiveをつけることで、スクロールのパフォーマンスに影響しないようにしています。

3つ目はイベントリスナーの解除です。表示した後は、5つのイベントリスナーを全部解除しています。不要になったリスナーを残しておくのは、メモリの無駄です。

注意点

実装で考えたことがあります。

「次のおすすめ」の内容は、今見ているページと関連するものにしてください。ソファのページを見ている人に、まったく関係ないカテゴリの商品を出しても意味がないです。同じカテゴリの別商品か、関連するコンテンツを出すのがいいです。

ポップアップではなく「カード」にしました。ポップアップはモーダルウィンドウのように画面の中央に出てきて、閉じるまで他の操作ができないことが多いです。これはユーザーにとってストレスです。右下の小さなカードなら、無視することもできるし、興味があれば押せます。

タブレットやスマホでは、touchstartを監視しています。マウスがないデバイスでも、タッチ操作がなければ「無操作」と判定します。

prefers-reduced-motionに対応して、フェードインのアニメーションを無効にしています。表示されるタイミングは同じですが、アニメーションなしでパッと表示されます。

実際の結果

このお客さまのプロジェクトで、A/Bテストを4週間やりました。

指標おすすめなし(A)おすすめあり(B)差分
回遊率(次ページへの遷移率)23.4%31.8%+8.4pt
おすすめカードのクリック率12.3%
商品ページの直帰率41.2%35.6%-5.6pt

回遊率が8.4ポイント上がりました。おすすめカードのクリック率は12.3%。10人に1人以上がクリックしてくれています。直帰率も5.6ポイント下がりました。「迷っている」タイミングで次のアクションを提示することで、離脱の代わりに回遊してくれるようになりました。ただし、これは1つのプロジェクトでの結果です。効果はコンテンツの種類やおすすめの精度によって変わります。

お客さまからは「押し付けがましくないのがいい」と言ってくれました。。

まとめ

ユーザーが「迷っている」瞬間は、チャンスでもあります。でも、ポップアップやモーダルで邪魔するのは逆効果です。「静かに、小さく、必要なときだけ」が大事。5秒の無操作検知は、その「必要なとき」を見つける1つの方法です。

今回のポイントは以下の3つです:

  • 5秒の無操作でフェードイン。マウス・スクロール・タッチ・キーボードの5イベントを監視
  • 一度だけ表示。閉じたら二度と出さない。passive: trueでパフォーマンスに配慮
  • ポップアップではなくカード。右下に小さく、無視もできる控えめなUI

迷っているユーザーに「次はこれ」と静かに教えることで、離脱を回遊に変えられます。実装は20行ぐらいのJSで、既存のページにすぐ追加できます。

株式会社ファストコーディングでは、こうしたユーザー行動に応じたUI演出やフロントエンドの実装サポートをしています。「商品ページの直帰率を下げたい」「回遊率を上げたい」という方は、お問い合わせフォームから気軽にご連絡ください。


※本記事は弊社外国人スタッフによる投稿です。言い回しや表現が不十分な個所がありますことご容赦いただきますようお願いいたします。