フォームの入力制御とバリデーション:「その他」自由記述とスムーズスクロール

前回は、GASから取得したデータを用いてチェックボックスを動的に組み立てるフロントエンド処理を解説しました。

実際のWebフォーム運用では、選択肢に用意されていない要望を拾うために「その他」項目を設けたり、必須項目が正しく入力されているかをチェックする「バリデーション」が不可欠です。

今回は、「その他」選択時のみテキスト入力欄を出現させるUI制御と、未入力項目がある場合に親切に該当箇所へスクロールさせるユーザビリティ向上の実装を解説します。

「その他」連動入力制御とエラー時スムーズスクロールの処理フロー

「その他」連動入力制御とエラー時スムーズスクロールの処理フロー

「その他」選択時のみテキスト欄を表示する動的制御

最初から「その他の詳細」入力欄が常に表示されていると、フォームが縦に長くなりユーザーに圧迫感を与えてしまいます。「その他」にチェックが入った時だけ入力欄を表示し、チェックが外れたら隠す設計が理想的です。

function setupOtherInputHandlers() {
  const fieldNames = ['category', 'service', 'trigger'];

  fieldNames.forEach(function(fieldName) {
    const checkboxes = document.querySelectorAll('input[name="' + fieldName + '"]');
    const otherInputContainer = document.getElementById(fieldName + '-other-input-container');
    const otherInput = document.getElementById(fieldName + '-other-input');

    if (!otherInputContainer || !otherInput) return;

    checkboxes.forEach(function(checkbox) {
      if (!checkbox.dataset.otherHandlerSet) {
        checkbox.addEventListener('change', function() {
          if (this.value === 'その他' && this.checked) {
            otherInputContainer.style.display = 'block';
          } else if (this.value === 'その他' && !this.checked) {
            otherInputContainer.style.display = 'none';
            otherInput.value = ''; // チェック解除時は入力内容をクリア
          }
        });
        checkbox.dataset.otherHandlerSet = 'true';
      }
    });
  });
}

チェックが外れた瞬間に otherInput.value = '' で入力文字を消去している点が大切です。誤って入力した文字が意図せず送信データに残ってしまう事故を防ぎます。

送信前の入力チェックとエラー箇所の特定

送信ボタンが押された際、必須項目が未入力であれば即座に処理を中断し、分かりやすいメッセージを表示します。

let hasError = false;
let firstErrorElement = null;

// お名前(テキスト)の必須チェック
const fullName = document.getElementById('fullName');
if (!fullName.value.trim()) {
  const errorEl = document.getElementById('fullName-error');
  errorEl.style.display = 'block';
  if (!firstErrorElement) firstErrorElement = errorEl;
  hasError = true;
}

// チェックボックスの選択チェック
const selectedCategories = Array.from(
  document.querySelectorAll('input[name="category"]:checked')
).map(cb => cb.value);

if (selectedCategories.length === 0) {
  const errorEl = document.getElementById('category-error');
  errorEl.style.display = 'block';
  if (!firstErrorElement) firstErrorElement = errorEl;
  hasError = true;
} else if (selectedCategories.includes('その他')) {
  // 「その他」が選ばれている場合の追加入力チェック
  const otherInput = document.getElementById('category-other-input');
  if (!otherInput || !otherInput.value.trim()) {
    const errorEl = document.getElementById('category-error');
    errorEl.textContent = '「その他」を選択した場合は、詳細内容を入力してください。';
    errorEl.style.display = 'block';
    if (!firstErrorElement) firstErrorElement = errorEl;
    hasError = true;
  }
}

単にチェックボックスが選択されているかだけでなく、「その他」が選ばれている場合は自由入力欄の記入まで必須にすることで、回答の抜け漏れを確実に防ぎます。

scrollIntoView によるスムーズスクロール案内

画面のどこかでエラーが起きた際、どこに問題があるか分からないとユーザーは離脱してしまいます。
エラーが見つかった最初のエレメント(firstErrorElement)の位置まで、画面を自動で滑らかにスクロールさせます。

if (hasError) {
  msg.textContent = '必須項目を入力してください。';
  btn.disabled = false;
  
  if (firstErrorElement) {
    setTimeout(function() {
      firstErrorElement.scrollIntoView({
        behavior: 'smooth',
        block: 'center'
      });
    }, 100);
  }
  return;
}

block: 'center' を指定することで、エラー箇所がスマートフォンの画面中央にピタリと表示され、ストレスのない修正を促すことができます。

「その他」の値を送信データへ統合するロジック

「その他」に入力されたテキストは、サーバーに渡す前に配列データへ綺麗にマージします。

function processOtherValues(selectedValues, fieldName) {
  const processedValues = [...selectedValues];
  const otherInput = document.getElementById(fieldName + '-other-input');
  if (otherInput && otherInput.value.trim()) {
    const index = processedValues.indexOf('その他');
    if (index !== -1) {
      processedValues[index] = 'その他: ' + otherInput.value.trim();
    }
  }
  return processedValues;
}

この前処理を行うことで、スプレッドシートには「その他: 〇〇希望」といった自然な形式で情報が1セルにまとまって記録されます。

まとめと次回の内容

今回は、ユーザーの入力体験を高める動的な入力制御と、丁寧なエラー案内を実現するバリデーション手法を解説しました。

次回は連載の最終回として、整えられたデータをGAS側で受け取り、ユニークIDを採番してスプレッドシートへ安全に保存する処理と、画面リロードなしで完了画面を表示・初期化するSPA風UIの実装を解説します。


eguchi.netをもっと見る

購読すると最新の投稿がメールで送信されます。