JavaScript | DOM 操作:イベント基礎 – input / change

JavaScript JavaScript
スポンサーリンク

input と change とは何か

input と change は、フォーム要素の値が変わったときに発火するイベントです。input は「値が変わるたびに逐次発火」、change は「確定したタイミングで一度だけ発火」します。ここが重要です:ライブ更新が欲しいなら input、確定後の処理(送信や検証確定)は change。日本語入力(IME)では input が未確定文字を含むため、必要に応じて composition 系イベントと組み合わせて扱います。


基本の違い(逐次 vs 確定)

テキスト入力での挙動

<input id="q" placeholder="検索">
<script>
  const q = document.getElementById("q");
  q.addEventListener("input",  () => console.log("途中:", q.value));
  q.addEventListener("change", () => console.log("確定:", q.value));
</script>
HTML

ここが重要です:input はキータイプごとに発火しリアルタイム。change はフォーカスが外れる、または Enter・確定操作の後に一度だけ。UI更新の速さが要るなら input、処理確定は change。

select/checkbox/radio での挙動

<select id="sel"><option>りんご</option><option>バナナ</option></select>
<input id="chk" type="checkbox">
<script>
  sel.addEventListener("change", () => console.log("選択:", sel.value));
  chk.addEventListener("change",  () => console.log("チェック:", chk.checked));
</script>
HTML

ここが重要です:選択系・チェック系は「操作した瞬間に change」が発火するため、逐次と確定の差は小さく見えます。テキストのような連続入力と区別して考えます。


日本語入力(IME)への対応

composition と組み合わせた安全な判定

<input id="name" placeholder="名前">
<script>
  let composing = false;
  name.addEventListener("compositionstart", () => composing = true);
  name.addEventListener("compositionend",   () => { composing = false; onCommit(); });
  name.addEventListener("input", () => {
    if (!composing) onLive(); // 未確定中は走らせない選択肢
  });
  function onLive() { console.log("ライブ:", name.value); }
  function onCommit() { console.log("確定:", name.value.trim()); }
</script>
HTML

ここが重要です:IME中は input が未確定を含みます。厳密な検証や検索実行は compositionend 後、ライブプレビューは composing を避けて走らせると誤動作が減ります。


ライブ更新の設計(input を活かす)

入力に応じてボタンの有効/無効を同期

<input id="email" type="email" required>
<button id="next" disabled>次へ</button>
<script>
  const email = document.getElementById("email");
  const next  = document.getElementById("next");
  email.addEventListener("input", () => {
    const v = email.value.trim();
    next.disabled = !(v && email.validity.valid);
  });
</script>
HTML

ここが重要です:視覚(文言・ボタン状態)と検証結果を“同じロジック”で同期させると、状態のズレが起きません。validity を活用すると簡潔です。

連続入力の負荷を抑える(デバウンス)

<input id="q" placeholder="検索">
<script>
  let timer;
  q.addEventListener("input", () => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      console.log("検索:", q.value.trim());
    }, 250); // 連続入力の最終のみ実行
  });
</script>
HTML

ここが重要です:毎キーで重い処理をすると体感が悪化。短い遅延で“入力が止まった”タイミングだけ実行するのが定石です。


確定処理の設計(change を活かす)

設定の保存を確定タイミングで行う

<select id="theme">
  <option value="light">明るい</option>
  <option value="dark">暗い</option>
</select>
<script>
  theme.addEventListener("change", () => {
    document.documentElement.dataset.theme = theme.value;
    console.log("テーマ保存:", theme.value);
  });
</script>
HTML

ここが重要です:操作確定時に一度だけ保存・送信・ロギングを行うのは change が適任。無駄な連続処理を避けられます。

ファイル選択の取り扱い

<input id="file" type="file" accept="image/*">
<script>
  file.addEventListener("change", () => {
    const f = file.files[0];
    if (!f) return;
    console.log("選択:", f.name, f.size);
  });
</script>
HTML

ここが重要です:file は change でのみ扱う。input は使えないので、選択確定後に検証・プレビューへ進めます。


よくある落とし穴と回避策

input 乱用で重くなる

毎キーでAPI呼び出しや再描画を走らせるとカクつきます。ここが重要です:デバウンス/スロットルを入れ、軽い UI 更新のみに留め、重い処理は確定(change or compositionend)に寄せます。

Enter で勝手に送信される

フォームの既定動作で Enter が submit になることがあります。ここが重要です:必要なら submit ハンドラで e.preventDefault、または Enter を個別に処理して“意図した送信”だけを許可します。

IME 未考慮のバリデーション誤爆

未確定の途中文字で“無効”判定をしてエラー表示を連発すると不快です。ここが重要です:composing 中は警告を抑える、確定後に厳密判定へ切り替える二段構えにします。

value の前後空白

入力値は trim して判定・送信するのが安全です。ここが重要です:見た目の空白で意図せず無効になるのを防ぎます。


実践例(検索ボックス、価格入力、チェックボックス依存)

検索:ライブ候補+確定検索(IME対応)

<input id="q" placeholder="検索">
<script>
  let composing = false, timer;
  q.addEventListener("compositionstart", () => composing = true);
  q.addEventListener("compositionend",   () => { composing = false; runSearch(); });
  q.addEventListener("input", () => {
    // ライブ候補(composing 中は遅延のみ)
    clearTimeout(timer);
    timer = setTimeout(() => {
      if (!composing) console.log("候補:", q.value);
    }, 150);
  });
  function runSearch() { console.log("確定検索:", q.value.trim()); }
</script>
HTML

価格入力:リアルタイム整形と確定保存

<input id="price" inputmode="decimal" placeholder="0">
<script>
  price.addEventListener("input", () => {
    const num = price.value.replace(/[^\d.]/g, "");
    price.value = num; // 入力中は簡易整形
  });
  price.addEventListener("change", () => {
    const v = parseFloat(price.value);
    if (Number.isFinite(v)) console.log("保存:", v);
    else console.log("数値ではありません");
  });
</script>
HTML

ここが重要です:入力中は“優しく整える”、確定時に“厳密に検証して保存”。体験と正確さを両立します。

チェックボックスに応じてボタンを有効化

<input id="agree" type="checkbox"> 規約に同意
<button id="next" disabled>次へ</button>
<script>
  agree.addEventListener("change", () => {
    next.disabled = !agree.checked;
  });
</script>
HTML

ここが重要です:選択系は change で十分。UI 状態を即同期します。


まとめ

input は“逐次”、change は“確定”。ライブな UI 更新や補助は input に、保存・送信・ロギングなどの確定処理は change に寄せる。日本語入力では compositionstart/end を用いて、未確定中の実行を避け、確定後に厳密処理。負荷の高い処理はデバウンスで間引き、フォーム既定動作と Enter の扱いを明確にする。これらを押さえれば、初心者でも快適で誤動作の少ない入力体験を設計できます。

タイトルとURLをコピーしました