UI/UX
投稿日:

長いフォームを怖くしない ─ 入力が揃ったら次のブロックがスッと出る「ステップ表示」の作り方

長いフォームを怖くしない ─ 入力が揃ったら次のブロックがスッと出る「ステップ表示」の作り方

こんにちは、BigViです。2月になりました。東京の冬は乾燥がすごいです。肌がカサカサになります。ホーチミンが恋しいです。。。

先日、クライアントから「フォームの途中でやめちゃう人が多い」という相談がありました。見たら、入力項目が15個もありました。全部が一度に見えます。私もそれ見て「うわ、こんなに書くの……」となりました。やる気がなくなります。

それで私が提案したのが「ステップ表示」です。最初は3〜4項目だけ見せます。それが埋まったら、次が下からスッと出てきます。項目の数は同じです。でも気持ちがだいぶ違います。

全部見えると「やめたい」になる

Baymard Instituteという研究機関が、2024年に調べたデータがあります。ECサイトのフォーム、離脱率が約69%です。すごい数字です。理由は「長すぎ」「複雑すぎ」が多いです。

私が思うのは、大事なのは「実際の項目数」じゃなくて「見えている項目数」です。最初にたくさん見えると、「あ、面倒だ」となります。だから、入力した分だけ次が出てくると、「あと少しだけ」という気持ちで進んでくれます。

ある案件でこの方法を試したとき、ディレクターの先輩に「それだとユーザーが全体量わからないのでは?」と聞かれました。それはそうです。だから私は、フォームの上に「ステップ 1 / 3」みたいな表示をつけました。全体がわかるし、目の前の入力だけに集中できます。これがいい感じです。

作り方の考え方

今回の作り方、シンプルです。大きなフレームワークは使いません。CSSトランジションとJavaScriptのinputイベントだけで作ります。

  • フォームを2〜3個の塊に分ける
  • 最初の塊だけ見せて、残りは隠す
  • 塊の中の必須が全部入力されたら、次の塊を下から20pxスライドで出す
  • アニメーションは200ms。速すぎないし遅すぎない

JSのコードも少ないです。それと、今あるフォームに後から追加できます。これうれしいです。

HTML構造

まずHTMLです。フォーム全体をformタグで囲んで、各塊をdivで分けます。data-stepで順番を管理します。

<form id="stepForm" class="step-form" novalidate>
  <!-- ステップ表示 -->
  <div class="step-indicator">
    <span class="step-current">1</span> / <span class="step-total">3</span>
  </div>

  <!-- 塊1: 基本情報 -->
  <div class="form-block is-visible" data-step="1">
    <h3>基本情報</h3>
    <label>
      お名前 <span class="required">*</span>
      <input type="text" name="name" required>
    </label>
    <label>
      メールアドレス <span class="required">*</span>
      <input type="email" name="email" required>
    </label>
  </div>

  <!-- 塊2: 詳細情報 -->
  <div class="form-block" data-step="2">
    <h3>ご要望について</h3>
    <label>
      会社名 <span class="required">*</span>
      <input type="text" name="company" required>
    </label>
    <label>
      お問い合わせ種別 <span class="required">*</span>
      <select name="type" required>
        <option value="">選択してください</option>
        <option value="estimate">お見積もり</option>
        <option value="consult">ご相談</option>
        <option value="other">その他</option>
      </select>
    </label>
  </div>

  <!-- 塊3: 送信 -->
  <div class="form-block" data-step="3">
    <h3>内容の確認</h3>
    <label>
      お問い合わせ内容 <span class="required">*</span>
      <textarea name="message" rows="4" required></textarea>
    </label>
    <button type="submit">送信する</button>
  </div>
</form>

requiredをつけた項目が「必須」です。JavaScriptがこの属性を見て判定します。HTMLの標準の属性なので、特別なものは要りません。

CSSでスライドの動きを作る

CSS、とてもシンプルです。隠れている塊をopacity: 0translateY(20px)で下にずらしておきます。クラスがついたら元に戻ります。

/* フォーム全体 */
.step-form {
  max-width: 480px;
  margin: 0 auto;
  font-family: sans-serif;
}

/* ステップ表示 */
.step-indicator {
  text-align: right;
  font-size: 13px;
  color: #6b7280;
  margin-bottom: 16px;
}

/* 各塊: 最初は隠す */
.form-block {
  opacity: 0;
  transform: translateY(20px);
  max-height: 0;
  overflow: hidden;
  transition: opacity 200ms ease-out,
              transform 200ms ease-out,
              max-height 300ms ease-out;
  pointer-events: none;
}

/* 見える状態 */
.form-block.is-visible {
  opacity: 1;
  transform: translateY(0);
  max-height: 500px;
  pointer-events: auto;
  margin-bottom: 24px;
}

/* ラベルと入力欄 */
.form-block label {
  display: block;
  margin-bottom: 12px;
  font-size: 14px;
  color: #374151;
}

.form-block input,
.form-block select,
.form-block textarea {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 8px 12px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font-size: 14px;
  box-sizing: border-box;
}

/* 必須マーク */
.required {
  color: #ef4444;
  font-size: 12px;
}

/* 送信ボタン */
.form-block button[type="submit"] {
  display: block;
  width: 100%;
  padding: 12px;
  background: #2563eb;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 15px;
  cursor: pointer;
  transition: background 150ms;
}

.form-block button[type="submit"]:hover {
  background: #1d4ed8;
}

/* アニメーション苦手な人への配慮 */
@media (prefers-reduced-motion: reduce) {
  .form-block {
    transition: opacity 50ms ease-out,
                max-height 50ms ease-out;
    transform: none;
  }
  .form-block.is-visible {
    transform: none;
  }
}

max-heightを使っています。height: autoだとトランジションが効かないです。ちょっとトリッキーですが、フォームの塊なら500pxで足ります。

prefers-reduced-motionも入れています。アニメーションが苦手な人は、ほぼ一瞬で切り替わります。私、これは毎回入れるようにしています。すごく大事です。

JavaScriptで出現をコントロールする

ここが一番大事の部分です。各塊の必須項目をリアルタイムで見て、全部埋まったら次の塊を出します。

<pre class="wp-block-syntaxhighlighter-code">document.addEventListener('DOMContentLoaded', function() {
  var form = document.getElementById('stepForm');
  if (!form) return;

  var blocks = form.querySelectorAll('.form-block');
  var stepCurrent = form.querySelector('.step-current');

  // 各塊の必須項目を監視
  blocks.forEach(function(block, index) {
    var requiredFields = block.querySelectorAll('[required]');

    requiredFields.forEach(function(field) {
      // input, change で監視(selectにも対応)
      field.addEventListener('input', function() {
        checkBlock(block, index);
      });
      field.addEventListener('change', function() {
        checkBlock(block, index);
      });
    });
  });

  // 塊の入力状態をチェック
  function checkBlock(block, index) {
    var requiredFields = block.querySelectorAll('[required]');
    var allFilled = true;

    requiredFields.forEach(function(field) {
      if (!field.value.trim()) {
        allFilled = false;
      }
      // メールは@があるかだけ見る
      if (field.type === 'email' && field.value.trim()) {
        if (field.value.indexOf('@') === -1) {
          allFilled = false;
        }
      }
    });

    // 全部埋まったら次の塊を出す
    if (allFilled && index + 1 < blocks.length) {
      var nextBlock = blocks[index + 1];
      if (!nextBlock.classList.contains('is-visible')) {
        nextBlock.classList.add('is-visible');
        updateStepIndicator(index + 2);
      }
    }
  }

  // ステップ表示を更新
  function updateStepIndicator(step) {
    if (stepCurrent) {
      stepCurrent.textContent = step;
    }
  }
});</pre>

コードは40行ぐらいです。ポイントを説明します。

inputとchangeの両方をつけている
selectinputイベントだと動きません。だから両方つけます。これでどの入力欄でも大丈夫です。

メールは@があるかだけ
ちゃんとしたチェックはサーバーでやります。ここで厳しくすると、入力の途中で塊が出たり消えたりします。それは気持ち悪いです。

一度出した塊は消さない
入力を消しても、次の塊はそのままです。私がわざとそうしました。消えたり出たりすると、ユーザーは「さっき書いたのどこ?」ってなります。それはよくないです。

実際のプロジェクトで気をつけること

この方法を案件で使ったとき、私がやってしまった失敗もあります。書きます。

塊の分け方が大事

最初のとき、私は1つの塊に必須を5個入れてしまいました。全部埋めないと次が出ない。そうすると、普通の長いフォームと同じです。リーダーに「それ意味ないよ」と言われました。。。

1塊の必須は2〜3個がちょうどいいです。少ない入力で「次が出た!」となるのがいいです。

スマホだと次の塊が見えない

PCは画面が大きいから大丈夫です。でもスマホ、次の塊が画面の下に行ってしまいます。ユーザーは出たことに気づかないかもしれません。

その場合、次の塊が出た後にスムーススクロールで少し下に動かすといいです。今回のコードではシンプルにしたかったので省きました。

JavaScriptが動かないとき

JSが動かないと、全部の塊が隠れたままです。困ります。

やり方は2つです。noscriptタグで全塊を見せるCSSを入れるか、HTMLで最初から全塊にis-visibleをつけておいて、JSで最初の塊以外を外す方法です。私は後の方法でやっています。こっちのほうがちゃんと動いてくれました。

A/Bテストで効果を確認する

作ったら、必ずA/Bテストしてください。私のプロジェクトでは3つの数字を比べました。

  • 途中離脱率:フォームを開いたけど送信しなかった人の割合
  • 平均入力時間:フォームを開いてから送信するまでの時間
  • 完了率:フォームを開いた人のうち、最後まで送信してくれた人の割合

結果ですが、ステップ表示を入れたほうがフォーム完了率が約18%上がりました。うれしいです。平均入力時間は少し伸びました。でもこれは、途中でやめないで最後まで書いてくれた人が増えたからです。いいことです。

Google AnalyticsやMicrosoft Clarityで、どの塊まで見えたかを見るといいです。2番目の塊がなかなか出ないなら、1番目の項目が多すぎるとか、入力しにくい項目があるとか。データが教えてくれます。

まとめ

今回の「ステップ表示」、長いフォームの「うわ、多い」という気持ちを減らす方法です。項目は減らしません。「見せ方」を変えるだけです。集める情報はそのままです。

大事なポイントは3つです。

  1. 1塊の必須は2〜3個まで:少ない入力で「次に進めた」と思ってもらう
  2. アニメーションは200ms:速すぎないし遅すぎない。いい感じです
  3. A/Bテストで比べる:途中離脱率、入力時間、完了率を見る

私の案件では完了率が約18%上がってくれました。フォームの内容とかターゲットで結果は変わります。でもCSS + JavaScript合わせて80行ぐらいです。試す価値はあると思います。

株式会社ファストコーディングでは、フォームのUX改善とかフロントエンドの実装サポートをしています。「フォームの離脱が多くて困っている」「もっと使いやすくしたい」という方は、こちらのお問い合わせフォームから気軽にご連絡ください。

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