【JavaScript入門】入力した文字数をリアルタイムに表示する(commentArea.js)

 ここでは 入力中のコメント文字数をリアルタイム表示 し、最大長を超えた場合に警告スタイルへ自動切替する commentArea.js を実装します。
日本語 IME 変換中はカウントを一時停止し、確定後にのみ更新することで「漢字変換途中で文字数が跳ねる」不快感を無くしています。さらに、入力が変わるたびに storage.jssaveCommentToStorage() を呼び、途中入力をローカルストレージへ即時保存して編集再開を快適にします。

フォルダ構成(該当部分)

node-js/
 └─ app/
     ├─ index.html
     ├─ style.css
     └─ js/
          ├─ common.js
          ├─ storage.js
          ├─ imageArea.js
          ├─ imageEffects.js
          ├─ controlArea.js
          ├─ commentArea.js   ← ★今回実装
          ├─ sendArea.js
          ├─ dateFormat.js
          └─ postListArea.js

commentArea.js ― フルコード & 詳細コメント

/* ---------------------------------------------------
   commentArea.js
   - コメント入力の文字数カウント
   - 日本語 IME 確定後にのみカウント
--------------------------------------------------- */
window.addEventListener('DOMContentLoaded', () => {

  /* ===== DOM キャッシュ ===== */
  const elTxt  = document.querySelector('#comment');        // textarea
  const elStat = document.querySelector('#commentStatus');  // 文字数表示
  const lenMax = parseInt(elTxt.dataset.lenmax);            // data-lenmax="100"

  let inComp = false;   // IME 変換フラグ: true=変換中 false=確定済

  /* ----- IME 制御 (composition*) ----- */
  // 変換開始: カウント停止
  elTxt.addEventListener('compositionstart', () => inComp = true);

  // 変換確定: カウント再開
  elTxt.addEventListener('compositionend', () => {
    inComp = false;
    updateStatus();     // 確定直後に必ず更新
  });

  /* ----- 通常入力 (input) ----- */
  elTxt.addEventListener('input', () => {
    if (!inComp) updateStatus();   // IME 中はスキップ
  });

  /* ----- 文字数計算 & 表示更新 ----- */
  function updateStatus(){
    // [...str] 展開でサロゲートペア対応 (Emoji = 1 文字)
    const len = [...elTxt.value].length;

    // CSS クラス切替: 普通 or 警告
    const cls = len <= lenMax
      ? 'inputStatus'
      : 'inputStatus inputStatusWarn';

    // ラベル書き換え
    elStat.innerHTML =
      `<span class="${cls}">(${len}文字 / 最大${lenMax}文字)</span>`;

    // ローカルストレージへ即時保存 (storage.js 側で定義)
    saveCommentToStorage();
  }

  // ページ読み込み時に初期カウントを表示
  updateStatus();
});

主要イベント・メソッド早見表

要素 / API働き使い方のポイント
compositionstartIME 変換開始ここで inComp = true
compositionendIME 確定ここで inComp = falseupdateStatus()
input値変更ごとに発火変換中フラグを見て処理分岐
[...str].lengthユニコードコードポイント数Emoji を 1 文字として数える
elStat.innerHTMLラベルの動的更新css クラスで色切替
saveCommentToStorage()入力内容を localStorage へ保存storage.js で実装済み

関数別の詳細解説

関数名処理内容
updateStatus()1. 現在の入力長をサロゲートペア対応で計算
2. 上限と比較し CSS クラスを切替 (inputStatusWarn は赤文字定義)
3. 文字数ラベルを更新
4. saveCommentToStorage() でローカル保存
匿名リスナ (input)キータッチやペーストで発火。inCompfalse のときのみ updateStatus() を呼ぶ
匿名リスナ (compositionstart / end)IME 状態をトグルし、確定直後に新しい文字列で再カウント

実装ポイント & ベストプラクティス

  1. IME イベントの活用
    変換中にカウントさせると「かな入力10文字→確定で1文字減」など挙動が不自然。composition* を利用するとスムーズ。
  2. サロゲートペア安全なカウント
    string.length では絵文字 😊 が 2 文字扱いになり誤判定。[...str] でコードポイント配列化すると 1 と数えられます。
  3. 即時保存で入力ロス防止
    1 文字ごとに saveCommentToStorage() を呼んでも、保存対象は短いテキストなのでパフォーマンス問題は皆無。クラッシュしても復帰が楽。
  4. CSS で警告色を一元管理
    文字数判定ロジックは JS、色や装飾は CSS 変数/クラスに委任すると後でテーマ変更しやすい。

これでコメント入力欄の UX が大幅に向上しました。
 次回はページ遷移なしで投稿データをサーバへ送信する sendArea.js を解説し、リアルタイム投稿フローを完成させます。