4日目のゴールと今日のテーマ
4日目は「バリデーションの設計を“拡張しやすい形”に進化させる日」です。
昨日までで、あなたはすでに
- 入力中の動的バリデーション
- touched による UX 改善
- 正規表現による形式チェック
- submit 時の全体チェック
を体験しました。
今日はここからさらに一歩進めて、
- バリデーションロジックを“再利用できる形”に整理する
- フィールドが増えても壊れない設計にする
- エラー表示を関数化して、render をスリムにする
- UX をさらに自然にする(エラーの遅延表示など)
という「設計力の強化」にフォーカスします。
バリデーションロジックを「共通化」して拡張しやすくする
なぜ共通化が必要なのか
昨日までのコードは、メールとパスワードのバリデーションを
それぞれの input / blur イベントの中で直接呼んでいました。
しかし、フィールドが増えるとこうなります。
- 名前
- 電話番号
- 郵便番号
- パスワード確認
- 生年月日
…イベントハンドラが増えるたびに、
「state 更新 → バリデーション → render」の流れを毎回書くことになります。
これは DRY(Don’t Repeat Yourself)違反 で、
コードが増えるほど壊れやすくなります。
共通バリデーション関数を作る
そこで、フィールド名を渡すと自動でバリデーションしてくれる関数を作ります。
function validateField(fieldName, value) {
switch (fieldName) {
case "email":
return validateEmail(value);
case "password":
return validatePassword(value);
default:
return "";
}
}
JavaScriptここでの深掘りポイントは、
- バリデーション関数を1カ所に集めることで、拡張が簡単になる
- フィールド名をキーにして、バリデーションを切り替える設計
という「中級の設計パターン」を体験できることです。
入力イベントを「共通ハンドラ」で扱う
イベントハンドラを1つにまとめる
昨日まではこうでした。
emailInputEl.addEventListener("input", ...)
passwordInputEl.addEventListener("input", ...)
JavaScript今日はこれを「共通化」します。
HTML 側に data-field 属性を付けます。
<input id="email" data-field="email" />
<input id="password" data-field="password" />
JavaScript で共通ハンドラを作ります。
function handleInput(event) {
const field = event.target.dataset.field;
const value = event.target.value;
state[field] = value;
if (state.touched[field]) {
state.errors[field] = validateField(field, value);
}
render();
}
JavaScriptそしてイベント登録をまとめます。
emailInputEl.addEventListener("input", handleInput);
passwordInputEl.addEventListener("input", handleInput);
JavaScriptblur も共通化できる
function handleBlur(event) {
const field = event.target.dataset.field;
state.touched[field] = true;
state.errors[field] = validateField(field, state[field]);
render();
}
emailInputEl.addEventListener("blur", handleBlur);
passwordInputEl.addEventListener("blur", handleBlur);
JavaScriptここでの重要ポイントは、
- フィールドが増えても handleInput と handleBlur を使い回せる
- バリデーションの追加は validateField に書くだけで済む
という「拡張性の高い設計」になっていることです。
エラー表示を関数化して render をスリムにする
render が太る問題
昨日までの render はこうでした。
emailErrorEl.textContent = state.errors.email;
passwordErrorEl.textContent = state.errors.password;
JavaScriptフィールドが増えると、render がどんどん太ります。
エラー表示を関数化する
function renderFieldError(fieldName, inputEl, errorEl) {
const error = state.errors[fieldName];
const touched = state.touched[fieldName];
errorEl.textContent = touched ? error : "";
if (error && touched) {
inputEl.classList.add("input-error");
} else {
inputEl.classList.remove("input-error");
}
}
JavaScriptrender の中ではこう書けます。
function render() {
renderFieldError("email", emailInputEl, emailErrorEl);
renderFieldError("password", passwordInputEl, passwordErrorEl);
submitButtonEl.disabled =
Boolean(state.errors.email || state.errors.password) ||
state.isSubmitting;
formMessageEl.textContent = state.errors.form;
}
JavaScriptここでの深掘りポイントは、
- render の責務を「画面更新」に限定できる
- エラー表示のロジックを1カ所に集められる
- フィールド追加時の変更箇所が最小限になる
という「設計の整理」ができていることです。
UX をさらに自然にする:エラーの遅延表示
入力中にすぐ赤くなるとストレスになる
例えばパスワードを入力するとき、
- 1文字目を入れた瞬間に「8文字以上にしてください」
- 2文字目でまた「8文字以上にしてください」
…と毎回赤くなると、ユーザーは「うるさい」と感じます。
遅延バリデーション(debounce)を導入する
入力が止まってから 300ms 後にバリデーションする、という UX 改善ができます。
let debounceTimer = null;
function handleInput(event) {
const field = event.target.dataset.field;
const value = event.target.value;
state[field] = value;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
if (state.touched[field]) {
state.errors[field] = validateField(field, value);
render();
}
}, 300);
}
JavaScriptここでの重要ポイントは、
- ユーザーが入力している最中はエラーを出さない
- 入力が止まったタイミングでエラーを出す
- UX が一気に自然になる
という点です。
submit 制御を「フォーム全体の状態」で判断する
フォーム全体のエラーをまとめて判定する
submit ボタンの disabled 判定を関数化します。
function hasFormError() {
return Boolean(state.errors.email || state.errors.password);
}
JavaScriptrender ではこう書けます。
submitButtonEl.disabled = hasFormError() || state.isSubmitting;
JavaScriptsubmit 時の流れを整理する
formEl.addEventListener("submit", (event) => {
event.preventDefault();
state.touched.email = true;
state.touched.password = true;
state.errors.email = validateEmail(state.email);
state.errors.password = validatePassword(state.password);
if (hasFormError()) {
state.errors.form = "入力内容を確認してください。";
render();
return;
}
state.errors.form = "";
state.isSubmitting = true;
render();
setTimeout(() => {
state.isSubmitting = false;
state.errors.form = "ログイン成功!(ダミー)";
render();
}, 800);
});
JavaScriptここでの深掘りポイントは、
- フォーム全体の状態を関数化して判断する
- submit の流れが読みやすくなる
- エラーがある場合の UX(フォーカス移動など)を追加しやすくなる
という「設計の整理」ができていることです。
4日目のまとめと、明日へのつなぎ
今日あなたが身につけたのは、まさに「設計力の中級ステップ」です。
- バリデーションロジックを共通化して拡張しやすくした
- 入力イベントと blur を共通ハンドラで扱えるようにした
- エラー表示を関数化して render をスリムにした
- 遅延バリデーションで UX を改善した
- submit 制御を「フォーム全体の状態」で判断するようにした
明日(5日目)はここからさらに、
- フィールド追加(パスワード確認・名前・電話番号など)を想定した設計
- バリデーションルールの外部化(設定オブジェクト化)
- エラー表示のテンプレート化(コンポーネント化)
など、「より大規模なフォームにも耐える設計」へ進みます。
今日のコードを触りながら、
- フィールドを1つ増やすと何が必要か
- どこを変えれば動くか
- どこが変わらなくて済むか
を考えてみると、設計力が一気に伸びます。


