blog

Contact Form 7に確認画面を追加する方法|プラグインを使わずJavaScriptで実装する手順

PUBLISHED 2026.09.04
Contact Form 7に確認画面を追加する方法|プラグインを使わずJavaScriptで実装する手順

WordPressの定番フォームプラグイン「Contact Form 7」(以下CF7)には、標準で入力内容の確認画面がありません。送信ボタンを押すと、確認をはさまずそのまま送信が完了します。

問い合わせフォームで確認画面がないと、誤送信や入力ミスが増えます。連絡先が間違っている、内容が途中で切れている、といった不完全な問い合わせにつながり、BtoBの見積依頼や申込フォームでは特に痛手です。

この記事では、CF7に「入力 → 確認 → 送信」の3ステップを追加する方法を解説します。結論から言うと、現在はプラグインではなくJavaScriptでの自作を推奨します。その理由も含めて説明します。


目次

  1. なぜ今、確認画面プラグインが推奨しにくいのか
  2. 実装の全体設計
  3. 共通スクリプト(JavaScript / CSS)
  4. フォーム側のHTML構造
  5. ステッパー(進捗表示)の実装
  6. 複数フォームへの横展開
  7. 実装時にハマりやすいポイント
  8. まとめ

1. なぜ今、確認画面プラグインが推奨しにくいのか

「CF7 確認画面」で検索すると、プラグインを使う方法が多くヒットします。しかし2026年現在、この選択肢には注意が必要です。

定番プラグインの開発終了

かつて定番だった「Contact Form 7 add confirm」は開発が終了しており、最新のCF7・WordPress環境では正常に動作しないケースが報告されています。長く使われてきたぶん、既存サイトの改修時に「入っているが動いていない」状態で見つかることもあります。

代替プラグインのキャッシュ問題

その代替として広く使われる「Contact Form 7 Multi-Step Forms」にも、無視できない事象が報告されています。他人が前回入力した確認画面の内容が表示されるというものです。開発者からは、サーバー側のキャッシュまたはキャッシュ系プラグインの設定が原因で、回避にはキャッシュの無効化が必要との説明がなされています。

確認画面には氏名・電話番号・金額といった個人情報や機密情報が載ります。それが別のユーザーに表示される可能性は、フォームの用途によっては致命的です。しかも回避策がキャッシュ無効化となると、サイト全体の表示速度に影響します。

結論:自作のほうが安全で自由度が高い

こうした背景から、実務ではJavaScriptによる自作が主流になりつつあります。余計なプラグインを増やさず、キャッシュ由来の情報混在リスクもなく、デザインも自由に調整できます。フォーム数が多い案件ほど、自作のメリットが大きくなります。


2. 実装の全体設計

保守性を考えると、役割を3つに分けるのが定石です。

役割置き場所内容
ロジックテーマまたはスニペット管理プラグインに1箇所確認画面の表示切替、値の収集、ステッパー制御
構造各CF7フォームの「フォーム」タブ入力エリア・確認エリア・ボタンの器
目印固定ページフォームを囲むラッパー

ポイントは、ロジックを1箇所に集約することです。フォームが複数ある場合、各フォームにスクリプトを直書きするとコピペだらけになり、1箇所の修正に何度も手を入れることになります。共通スクリプトを1本用意し、各フォームには「確認画面あり」の目印だけを付ける構成にしておけば、修正は常に1箇所で済みます。

置き場所は、テーマを直接触りたくない場合はWPCodeなどのスニペット管理プラグインが便利です。テーマ更新の影響を受けず、管理画面から編集できます。子テーマがあるならそちらでも構いません。


3. 共通スクリプト(JavaScript / CSS)

jQuery前提のコードです。CF7自体がjQueryを利用する構成のため、多くのテーマではすでに読み込まれています。

CSS

css

.cf7-confirm-screen { display: none; }
.cf7c-active .cf7-input-screen { display: none; }
.cf7c-active .cf7-confirm-screen { display: block; }

.cf7-confirm-screen .cf7c-list { border-top: 1px solid #ddd; }
.cf7-confirm-screen .cf7c-row {
  display: flex; border-bottom: 1px solid #ddd; padding: 12px 4px;
}
.cf7-confirm-screen .cf7c-label {
  width: 38%; font-weight: bold; padding-right: 12px;
}
.cf7-confirm-screen .cf7c-value { width: 62%; white-space: pre-wrap; }

.cf7c-buttons { margin-top: 20px; }
.cf7c-back { background: #999 !important; }

.cf7c-active というクラスの有無だけで入力画面と確認画面を切り替える設計です。要素の生成・破棄をせず表示を切り替えるだけなので、「修正する」で戻ったときに入力値がそのまま保持されます。

JavaScript

javascript

document.addEventListener('DOMContentLoaded', function () {
  if (typeof jQuery === 'undefined') return;
  jQuery(function ($) {

    $('.cf7-has-confirm').each(function () {
      var $wrap    = $(this);
      var $form    = $wrap.find('form.wpcf7-form');
      var $confirm = $wrap.find('.cf7-confirm-screen');
      var $list    = $confirm.find('.cf7c-list');

      // 「確認する」クリック
      $wrap.on('click', '.cf7-confirm-btn', function (e) {
        e.preventDefault();

        // 必須チェック(ブラウザ標準の警告を利用)
        var formEl = $form.get(0);
        if (formEl && typeof formEl.reportValidity === 'function') {
          if (!formEl.reportValidity()) return;
        }

        $list.empty();

        $form.find('.wpcf7-form-control-wrap').each(function () {
          try {
            var $ctrlWrap = $(this);
            if ($ctrlWrap.find('input[type=file]').length) return;

            var $field = $ctrlWrap.closest('p, .cf7c-field');
            var label  = getLabel($field, $ctrlWrap);
            var val    = getValue($ctrlWrap);

            if (val === '' || val === null) return; // 未入力は表示しない
            appendRow(label, val);
          } catch (err) {
            console.error('確認画面: 項目処理でエラー', err, this);
          }
        });

        $wrap.addClass('cf7c-active');

        // 送信ボタンが見える位置までスクロール
        setTimeout(function () {
          var $btns = $wrap.find('.cf7c-buttons.cf7-confirm-screen');
          if ($btns.length) {
            var target = $btns.offset().top
                       - ($(window).height() - $btns.outerHeight() - 40);
            $('html, body').animate({ scrollTop: Math.max(target, 0) }, 300);
          }
        }, 50);
      });

      // 「修正する」クリック
      $wrap.on('click', '.cf7-back-btn', function (e) {
        e.preventDefault();
        $wrap.removeClass('cf7c-active');
        $('html, body').animate({ scrollTop: $wrap.offset().top - 40 }, 300);
      });

      // 送信完了後は状態をリセット
      var formEl = $form.get(0);
      if (formEl) {
        formEl.addEventListener('wpcf7mailsent', function () {
          $wrap.removeClass('cf7c-active');
          $list.empty();
        });
      }

      // --- ヘルパー ---
      function getLabel($field, $ctrlWrap) {
        var explicit = $ctrlWrap.attr('data-confirm-label');
        if (explicit) return explicit;
        if (!$field.length) return '';
        var clone = $field.clone();
        clone.find('.wpcf7-form-control-wrap, script, style, br').remove();
        return $.trim(clone.text()).replace(/\s+/g, ' ').replace(/[::]\s*$/, '');
      }

      function getValue($ctrlWrap) {
        var vals = [];
        var $checked = $ctrlWrap.find(
          'input[type=checkbox]:checked, input[type=radio]:checked'
        );
        if ($checked.length) {
          $checked.each(function () {
            var $lab = $(this).closest('label');
            vals.push($lab.length ? $.trim($lab.text()) : $(this).val());
          });
          return vals.join('、');
        }
        var $inp = $ctrlWrap.find(
          'input[type=text], input[type=email], input[type=tel], ' +
          'input[type=number], input[type=date], textarea, select'
        );
        $inp.each(function () {
          var v = $(this).val();
          if (v != null && $.trim(String(v)) !== '') vals.push(v);
        });
        return vals.join('、');
      }

      function appendRow(label, value) {
        var $row = $('<div class="cf7c-row"></div>');
        $row.append($('<div class="cf7c-label"></div>').text(label || ''));
        $row.append($('<div class="cf7c-value"></div>').text(value));
        $list.append($row);
      }
    });
  });
});

コードのポイントを3つ挙げます。

各項目の処理を try/catch で囲む。 フォームの構造は項目ごとに微妙に異なります。1項目でエラーが起きるとループ全体が停止し、「途中までしか確認画面に出ない」という分かりにくい不具合になります。個別に囲んでおけば、問題のある項目だけスキップして残りは正常に表示されます。

チェックボックス・ラジオはラベル文字を取得する。 value をそのまま出すと記号や英数字が表示されることがあります。ユーザーが実際に見て選んだ文字列を出すべきなので、label 要素のテキストを拾います。

確認画面表示後、送信ボタンが見える位置までスクロールする。 これは実務上かなり重要です。確認画面に切り替わっても見た目が入力画面と似ていると、ユーザーは「まだ入力が続いている」と誤解します。送信ボタンを視界に入れることで、次のアクションが明確になります。setTimeout を挟んでいるのは、表示後の高さが確定してから位置を計算するためです。


4. フォーム側のHTML構造

CF7の「フォーム」タブに、次の構造で記述します。

html

<div class="cf7-input-screen">

  <p>お名前<br>[text* your-name]</p>
  <p>メールアドレス<br>[email* your-email]</p>
  <p>お問い合わせ内容<br>[textarea your-message]</p>

</div>

<div class="cf7-confirm-screen">
  <h3>入力内容の確認</h3>
  <div class="cf7c-list"></div>
</div>

<div class="cf7c-buttons cf7-input-screen">
  <button type="button" class="cf7-confirm-btn wpcf7-form-control wpcf7-submit">確認する</button>
</div>

<div class="cf7c-buttons cf7-confirm-screen">
  <button type="button" class="cf7-back-btn wpcf7-form-control wpcf7-submit cf7c-back">修正する</button>
  [submit "送信する"]
</div>

.cf7c-list の中身はJavaScriptが生成するため、空のまま置いておきます。

さらに、固定ページ側でショートコードを囲みます。

html

<div class="cf7-has-confirm">
  [contact-form-7 id="xxxx" title="お問い合わせ"]
</div>

このラッパーが、スクリプトの処理対象を判別する目印になります。ブロックエディタを使っている場合は「カスタムHTML」ブロックに記述してください。段落ブロックに貼るとタグがそのまま表示されたり、自動整形で壊れたりします。


5. ステッパー(進捗表示)の実装

「1 入力 → 2 確認 → 3 送信」の進捗表示があると、ユーザーは現在地を把握しやすくなります。

html

<div class="cf7-steps">
  <span class="cf7-step is-active" data-step="1"><span class="num">1</span>入力</span>
  <span class="cf7-bar"></span>
  <span class="cf7-step" data-step="2"><span class="num">2</span>確認</span>
  <span class="cf7-bar"></span>
  <span class="cf7-step" data-step="3"><span class="num">3</span>送信</span>
</div>

制御はスクリプト側に関数を追加します。

javascript

var $steps = $wrap.find(‘.cf7-steps’); function setStep(n) { if (!$steps.length) return; $steps.find(‘.cf7-step’).each(function () { var s = parseInt($(this).attr(‘data-step’), 10); $(this).toggleClass(‘is-active’, s === n).toggleClass(‘is-done’, s < n); }); }

あとは「確認する」で setStep(2)、「修正する」で setStep(1)wpcf7mailsent イベントで setStep(3) を呼ぶだけです。

なお、テーマ側にすでにステッパーのデザインがある場合、そのままではスクリプトと連動しません。各ステップ要素に class="cf7-step" data-step="1" のような属性を付与すれば制御対象になります。


6. 複数フォームへの横展開

この設計の利点は、フォームが増えても作業が増えにくいことです。

2つ目以降のフォームでは、共通スクリプトはそのまま使い、フォーム側に確認エリアとボタンの構造を追加し、固定ページでラッパーで囲む——これだけで済みます。項目名や項目数が違っても、スクリプトが自動で拾うため個別対応は不要です。

サイト内に複数の申込フォームがあるケースでは、この差が大きく効いてきます。


7. 実装時にハマりやすいポイント

キャッシュは必ず除外する

自作方式でもキャッシュには注意が必要です。フォームページがキャッシュされると、確認画面の挙動が不安定になったり、CF7のnonceが古くなって送信が失敗したりします。キャッシュ系プラグインやサーバーキャッシュの設定で、フォームページを除外してください。

見出しの自動取得がずれる場合がある

スクリプトは各項目の見出しを周辺のテキストから推測します。レイアウトによっては意図しない文字列を拾うことがあります。その場合は、該当する項目だけ明示指定すれば解決します。

html

<p>ご希望の日程
  <span class="cf7c-field" data-confirm-label="ご希望の日程">
    [date your-date]
  </span>
</p>

全項目に付ける必要はありません。ずれた箇所だけ後から補強する運用で十分です。

ラジオボタンは解除できない

CF7のラジオボタンは、一度選択すると未選択に戻せません。任意項目にラジオを使うと、誤って選んだユーザーが取り消せず、実質的に必須項目のように振る舞ってしまいます。

単一選択かつ任意にしたい場合は、チェックボックスに exclusive オプションを付けるのが有効です。「1つだけ選べる、選ばないこともできる」という挙動になります。

[checkbox your-choice use_label_element exclusive "選択肢A" "選択肢B" "選択肢C"]

修正後はキャッシュを消して確認する

スクリプトを更新したら、ブラウザのスーパーリロード(Windows: Ctrl+F5 / Mac: Cmd+Shift+R)で古いJavaScriptを破棄してから動作確認してください。「直したのに変わらない」の大半はこれが原因です。


8. まとめ

CF7に確認画面を追加する方法として、現在はJavaScriptによる自作を推奨します。

  • 定番だった確認画面プラグインは開発終了、代替プラグインにもキャッシュ由来の表示混在リスクがある
  • 自作ならプラグインを増やさず、キャッシュ設定の制約も受けにくい
  • ロジックを1箇所に集約すれば、フォームが複数あっても保守は1箇所で済む
  • 確認画面では「送信ボタンを視界に入れる」配慮が、離脱と誤解の防止に効く

確認画面の有無は、フォームの完了率と問い合わせ内容の精度に直結します。項目数の多いフォームほど効果が大きいため、申込・見積依頼系のフォームでは導入を検討する価値があります。


フォームの実装やWordPressサイトの改修についてご相談がありましたら、お気軽にお問い合わせください。