条件分岐フォーム(Conditional Form)— 表示切替

フォーム入力 初級

このコンポーネントについて

条件分岐フォームは、ある項目の選択内容に応じて後続の入力項目を表示・非表示に切り替えるUIパターンです。アンケートフォームや申請フォームなど、「特定の選択をしたときだけ追加情報を入力させたい」場面で広く使われます。このページでは、ラジオボタンで自由記入欄の表示を切り替えるパターンと、チェックボックスで住所入力ブロックの表示を切り替えるパターンの2種類を確認・コピペできます。非表示にした項目は値をクリアしてdisabledにすることで、送信データに含めない実装を徹底しています。

  • 選択連動による項目の表示・非表示切替 — ラジオボタン・チェックボックスの選択状態に応じてhidden属性を付け外しする
  • 非表示項目の送信対象からの除外 — 非表示にした項目は値をクリアしdisabled化・required解除することで、フォーム送信データに含めない
  • 実際の送信動作で正しさを実演<form>submitイベントで実際にブラウザ標準バリデーションを走らせ、非表示項目のrequiredが送信をブロックしないことを確認できる

実装のポイント・注意点

条件分岐フォームで一番ハマりやすいのが「非表示にした項目の扱い」です。display: nonehidden属性で見た目を消しただけでは、フォーム内の<input><textarea>は値が残ったまま送信データに含まれてしまいます。このデモでは、項目を非表示にするタイミングで必ず値をクリアし、disabled属性を付与することで送信対象から確実に除外しています。

もう1つの注意点がrequired属性です。非表示にした項目にrequiredを残したままにすると、ブラウザの標準バリデーション(checkValidity())が「画面に見えていない入力欄」をエラー対象にしてしまい、フォームの送信自体が止まってしまいます。項目を表示するときはrequiredを付与し、非表示にするときはremoveAttribute('required')で解除する、という切り替えを表示・非表示のロジックとセットで行うのがポイントです。

このデモでは送信ボタンをtype="submit"にして<form>submitイベントをpreventDefault()で受け取る構成にしています。そのため、ブラウザ標準のバリデーションUI(吹き出しメッセージ)がそのまま動作し、「非表示項目のrequiredが正しく解除されていれば送信がブロックされない」ことを実際のフォーム送信の挙動として確認できます。

HTML・CSS・バニラJavaScriptのみで実装しており、フレームワーク不要でコピペすぐに動きます。

デモ

Pattern 1 — お問い合わせ種別(ラジオボタン連動)

お問い合わせ種別を選択してください

Pattern 2 — 請求先住所(チェックボックス連動)

サンプルソース

3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.js — 保存時の文字コードは UTF-8 を指定してください(Shift-JISだと日本語が文字化けします)。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>条件分岐フォーム サンプル</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>

<!-- Pattern 1: お問い合わせ種別(ラジオボタン連動) -->
<form class="cond-form" id="inquiry-form" novalidate>
  <p class="cond-question">お問い合わせ種別を選択してください</p>
  <div class="cond-radio-group">
    <label class="cond-radio-label">
      <input type="radio" name="inquiry-type" value="document"> 資料請求
    </label>
    <label class="cond-radio-label">
      <input type="radio" name="inquiry-type" value="quote"> 見積依頼
    </label>
    <label class="cond-radio-label">
      <input type="radio" name="inquiry-type" value="other"> その他
    </label>
  </div>
  <textarea id="inquiry-detail" class="cond-textarea" rows="3"
            placeholder="お問い合わせ内容を具体的にご記入ください" hidden disabled></textarea>
  <div class="cond-controls">
    <button type="submit" class="cond-submit-btn">送信</button>
    <button type="button" class="reset-btn" onclick="resetInquiryForm()">リセット</button>
  </div>
  <div class="cond-result" id="inquiry-result" aria-live="polite"></div>
</form>

<!-- Pattern 2: 請求先住所(チェックボックス連動) -->
<form class="cond-form" id="billing-form" novalidate>
  <label class="cond-checkbox-label">
    <input type="checkbox" id="billing-differs"> 請求先住所が配送先と異なる
  </label>
  <div class="cond-billing-block" id="billing-block" hidden>
    <input type="text" id="billing-zip" class="cond-input" placeholder="郵便番号(例:123-4567)" disabled>
    <input type="text" id="billing-address" class="cond-input" placeholder="住所" disabled>
    <input type="text" id="billing-name" class="cond-input" placeholder="宛名" disabled>
  </div>
  <div class="cond-controls">
    <button type="submit" class="cond-submit-btn">送信</button>
    <button type="button" class="reset-btn" onclick="resetBillingForm()">リセット</button>
  </div>
  <div class="cond-result" id="billing-result" aria-live="polite"></div>
</form>

<script src="./script.js"></script>
</body>
</html>
:root {
  --cond-accent: #2B7FE8;
  --cond-bg-disabled: #f4f6f9;
  --cond-border: #d0d7e0;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  font-family: sans-serif;
  padding: 24px;
  background: #f0f2f5;
  color: #1a2332;
}

.cond-form {
  max-width: 480px;
  margin: 0 auto 32px;
  background: #fff;
  padding: 24px;
  border-radius: 8px;
}

.cond-question {
  font-weight: 600;
  margin: 0 0 12px;
}

.cond-radio-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}

.cond-radio-label,
.cond-checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

/* hidden属性はブラウザ標準で display: none になるが、
   flex/blockを持つ要素はCSSの詳細度で上書きされる場合があるため明示しておく */
.cond-textarea[hidden],
.cond-billing-block[hidden] {
  display: none;
}

.cond-textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1.5px solid var(--cond-border);
  border-radius: 6px;
  font-family: sans-serif;
  font-size: 14px;
  resize: vertical;
  margin-bottom: 12px;
}

.cond-textarea:disabled,
.cond-input:disabled {
  background: var(--cond-bg-disabled);
  color: #9aa5b4;
}

.cond-billing-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}

.cond-input {
  padding: 10px 12px;
  border: 1.5px solid var(--cond-border);
  border-radius: 6px;
  font-family: sans-serif;
  font-size: 14px;
}

.cond-checkbox-label {
  margin-bottom: 12px;
}

.cond-controls {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

.cond-submit-btn {
  padding: 8px 24px;
  background: var(--cond-accent);
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  font-family: sans-serif;
  transition: background 0.15s;
}

.cond-submit-btn:hover {
  background: #1a6ed4;
}

.cond-result {
  margin-top: 12px;
  font-size: 14px;
}

.cond-result.has-value {
  border: 1px solid var(--cond-border);
  border-radius: 6px;
  padding: 12px;
  background: #fafbfc;
}
// ===== Pattern 1: お問い合わせ種別(ラジオボタン連動) =====

var inquiryForm = document.getElementById('inquiry-form');
var inquiryDetail = document.getElementById('inquiry-detail');
var inquiryResult = document.getElementById('inquiry-result');

// ラジオボタンの選択状態に応じて自由記入欄の表示・disabled・requiredを切り替える
document.querySelectorAll('input[name="inquiry-type"]').forEach(function (radio) {
  radio.addEventListener('change', function () {
    if (radio.value === 'other' && radio.checked) {
      inquiryDetail.hidden = false;
      inquiryDetail.disabled = false;
      inquiryDetail.setAttribute('required', '');
    } else if (radio.checked) {
      // 非表示にするときは値クリア+disabled+required解除をセットで行う
      inquiryDetail.hidden = true;
      inquiryDetail.value = '';
      inquiryDetail.disabled = true;
      inquiryDetail.removeAttribute('required');
    }
  });
});

inquiryForm.addEventListener('submit', function (e) {
  e.preventDefault();
  if (!inquiryForm.checkValidity()) {
    inquiryForm.reportValidity();
    return;
  }
  var checked = inquiryForm.querySelector('input[name="inquiry-type"]:checked');
  var labelMap = { document: '資料請求', quote: '見積依頼', other: 'その他' };
  var text = '種別: ' + labelMap[checked.value];
  if (checked.value === 'other') {
    text += ' / お問い合わせ内容: ' + inquiryDetail.value;
  }
  inquiryResult.textContent = '送信しました(' + text + ')';
  inquiryResult.classList.add('has-value');
});

function resetInquiryForm() {
  inquiryForm.reset();
  inquiryDetail.hidden = true;
  inquiryDetail.value = '';
  inquiryDetail.disabled = true;
  inquiryDetail.removeAttribute('required');
  inquiryResult.textContent = '';
  inquiryResult.classList.remove('has-value');
}

// ===== Pattern 2: 請求先住所(チェックボックス連動) =====

var billingForm = document.getElementById('billing-form');
var billingDiffers = document.getElementById('billing-differs');
var billingBlock = document.getElementById('billing-block');
var billingResult = document.getElementById('billing-result');
var billingFields = ['billing-zip', 'billing-address', 'billing-name'].map(function (id) {
  return document.getElementById(id);
});

// チェック状態に応じて住所ブロックの表示・disabled・requiredを切り替える
billingDiffers.addEventListener('change', function () {
  if (billingDiffers.checked) {
    billingBlock.hidden = false;
    billingFields.forEach(function (field) {
      field.disabled = false;
      field.setAttribute('required', '');
    });
  } else {
    // 非表示にするときは値クリア+disabled+required解除をセットで行う
    billingBlock.hidden = true;
    billingFields.forEach(function (field) {
      field.value = '';
      field.disabled = true;
      field.removeAttribute('required');
    });
  }
});

billingForm.addEventListener('submit', function (e) {
  e.preventDefault();
  if (!billingForm.checkValidity()) {
    billingForm.reportValidity();
    return;
  }
  var text;
  if (billingDiffers.checked) {
    text = '請求先住所: ' + billingFields[0].value + ' ' + billingFields[1].value + ' ' + billingFields[2].value + ' 様';
  } else {
    text = '請求先住所: 配送先と同じ';
  }
  billingResult.textContent = '送信しました(' + text + ')';
  billingResult.classList.add('has-value');
});

function resetBillingForm() {
  billingForm.reset();
  billingBlock.hidden = true;
  billingFields.forEach(function (field) {
    field.value = '';
    field.disabled = true;
    field.removeAttribute('required');
  });
  billingResult.textContent = '';
  billingResult.classList.remove('has-value');
}

AI用プロンプト

各パターンのプロンプトをコピーしてAIに渡すと、同様のコンポーネントを生成できます。

ChatGPTやClaudeにこのプロンプトを渡すと、同様のコンポーネントをゼロから生成・カスタマイズできます。項目の追加や文言変更など、要件を追記して使うのがおすすめです。

※ このプロンプトを使ってもデモとまったく同じ動作にならない場合があります。AIの解釈や生成タイミングによって差が出ることをご了承ください。

💡 jQuery・Vue・React など特定のライブラリで実装したい場合は、プロンプトの末尾に「〇〇を使って実装してください」と追記してください。

Pattern 1 — お問い合わせ種別(ラジオボタン連動)

# 条件分岐フォーム(ラジオボタン連動・自由記入欄)作成依頼

## 概要
ラジオボタンで「その他」を選んだときだけ自由記入欄を表示する条件分岐フォームを実装してください。

## 要件
- 「お問い合わせ種別を選択してください」に対して「資料請求」「見積依頼」「その他」の3択ラジオボタンを表示する
- 「その他」を選択したとき、下部の自由記入欄(テキストエリア)を表示し、入力必須(required)にする
- 「資料請求」または「見積依頼」を選択したとき、自由記入欄を非表示にし、入力内容をクリアしたうえで送信対象から除外する(disabled化・required解除)
- 送信ボタン押下時にフォームのバリデーションを行い、「その他」選択中に自由記入欄が空の場合は送信をブロックする
- バリデーションを通過した場合は、選択した種別と自由記入欄の内容(表示時のみ)を画面に表示する(実際のページ遷移は行わない)
- リセットボタンで選択・入力内容・表示状態を初期状態に戻す

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:必要

## 動作詳細
非表示にした項目は display: none だけでなく、必ず disabled 属性と required 属性の解除もセットで行うこと。
disabled にしないと非表示のまま値が送信データに残ってしまい、required を外さないと非表示のフィールドがバリデーションエラーで送信をブロックしてしまう。
フォームの送信は <form> の submit イベントを preventDefault() で捕捉し、実際のページ遷移はさせずに結果を画面に表示すること。

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。

Pattern 2 — 請求先住所(チェックボックス連動)

# 条件分岐フォーム(チェックボックス連動・住所ブロック)作成依頼

## 概要
チェックボックスをONにしたときだけ住所入力ブロックを表示する条件分岐フォームを実装してください。

## 要件
- 「請求先住所が配送先と異なる」チェックボックスを1つ表示する
- チェックをONにしたとき、郵便番号・住所・宛名の3つの入力欄を表示し、いずれも入力必須(required)にする
- チェックをOFFにしたとき、3つの入力欄を非表示にし、入力内容をクリアしたうえで送信対象から除外する(disabled化・required解除)
- 送信ボタン押下時にフォームのバリデーションを行い、ONの状態で3項目のいずれかが空の場合は送信をブロックする
- バリデーションを通過した場合は、OFFなら「配送先と同じ」、ONなら入力した郵便番号・住所・宛名を画面に表示する(実際のページ遷移は行わない)
- リセットボタンでチェック状態・入力内容・表示状態を初期状態に戻す

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:必要

## 動作詳細
非表示にした項目は display: none だけでなく、必ず disabled 属性と required 属性の解除もセットで行うこと。
disabled にしないと非表示のまま値が送信データに残ってしまい、required を外さないと非表示のフィールドがバリデーションエラーで送信をブロックしてしまう。
フォームの送信は <form> の submit イベントを preventDefault() で捕捉し、実際のページ遷移はさせずに結果を画面に表示すること。

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。