裏紙に書く程度の内容

javascriptで数値の入力を自動的にカンマ区切りにする方法

入力欄に数値が入力されたらテキストを取得し、カンマ区切りした文字列に変換する方法です。

使用するイベント

入力欄に文字列が入力されたことを検知するには、keydown,keypress,keyup等が思いつきます。

ついでなのでこれらの違いを勉強しておきます。

keydown イベント

キーを押すと発火します。押しっぱなしにすると発火を繰り返します。

keypress イベント

テキストが挿入された時に発火します。押しっぱなしで繰り返します。

keydownとの違いは”挿入された時”ということ。なので、shiftキー等、文字が挿入されないキーを押しても反応しません。

keyup イベント

キーを離したときに発火します。文字入力されない、shiftキー等も発火します。


検知したいのは入力欄の値が変化したときです。keypressだとBS,Del等で文字を消したときに検知できないのでNGです。

keydown,keypressどちらでもいけますが、押しっぱなしの時まで自動カンマ区切りしなくてもいい気がするのでkeyupを使うことにします。

イベントの作成

実際にカンマを自動補完するイベントを作成します。

カンマを補完するイベント


$("#target-text").bind("keyup", function() {
    var newVal, val;
    val = $(this).val().replace(/,/g, "");                  // (1)
    newVal = val.replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,"); // (2)
    $(this).val(newVal);                                    // (3)
});

簡単に説明すると、

(1)で入力値を取得します。この時、既に入っているカンマは除去しておきます。
(2)で3ケタごとにカンマを補完し、(3)で元の入力欄に戻します。

ちなみに1行にするとこんな感じ

$(this).val($(this).val().replace(/,/g, "").replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,"));

発火の制限

上記イベントの設定では、どんなキーを押しても入力値の変換が行われます。
カーソルキーで移動してもイベントは発火し、入力欄が更新されてカーソルはテキストの後ろになるため移動できません。

また、ctrl + a等も使えない状態です。

このままだと不便なので、実際の値を更新するのは入力値が変化したときだけ、という条件を加えます。

その方法として考えられるのは、直前の値の状態を保持しておくか、特定のキーにのみ反応するようにします。

ここでは後者の方法でやってみます。

押されたキーを判別して制御

押されたキーの判別にはkeyCodeを使います。
必要なキーは数値の0~9と、del,bsくらいでしょうか。それぞれのキーコードは下記の通りです。

  • 0~9   : 48 ~ 57
  • bs     : 8
  • del    : 46

これら以外の入力では変換処理を行わないようにしてみます。

$("#target-text").bind("keyup", function(e) {
    var newVal, val;
    if ((e.keyCode >= 48 && e.keyCode <= 57) || e.keyCode 

===

 8 || e.keyCode 

===

 46) {
      val = $(this).val().replace(/,/g, "");
      newVal = val.replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
      $(this).val(newVal);
    }
});

これで、数値及び削除キーの場合のみカンマの補完が行われます。

カーソル位置の制御

現状ではまだ問題があります。

矢印のキーで移動して入力した場合、カンマにより入力値が更新され、カーソルはテキストの末尾に来ます。
この状態だと使いにくいので、カーソルの位置を制御します。

カーソルの位置は、エレメントのselectionStartで取得できます。

カンマ編集時に初期状態のカーソル位置を記憶しておき、編集後、その位置に戻すことで対処できそうです。

但し、カンマ編集により文字数が増えていたら、その分後ろに下げるようにします。

$("#target-text").bind("keyup", function(e) {
  var input, newPosition, newVal, orgLength, orgVal, position, val;

  if ((e.keyCode >= 48 && e.keyCode <= 57) || e.keyCode 

===

 8 || e.keyCode 

===

 46) {

    // (1)
    input = $(this).get(0);
    position = input.selectionStart;

    // (2)
    orgVal = $(this).val();
    orgLength = orgVal.length;

    // (3)
    val = orgVal.replace(/,/g, "");
    newVal = val.replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
    $(this).val(newVal);

    // (4)
    newPosition = position + newVal.length - orgLength;
    input.selectionStart = newPosition;
    input.selectionEnd = newPosition;
  }
});

(1)でエレメント、及びカーソル位置を取得しています。
(2)で編集前の文字長さを記憶しておきます。
(3)では前述までのカンマ編集をしています。

(4)で編集前後の文字の長さを比較し、カーソル位置を調整します。

実際に動かしてみるとこんな感じ。

数値以外入力できないようにする

さらに数値以外の入力を無効化する場合。

前述ではkeyupのイベントを使っているので、数値以外の入力があった場合はカンマ編集処理に来た時点で入力されちゃっています。

なので、イメージ的には数値以外の入力値を”ブロックする”のではなく、”後から除去する”って感じです。

既に入力値からカンマを除去するようにしているのでここをちょっと変えればできそうです。

// 変更前
// val = orgVal.replace(/,/g, "");
// 変更後
val = orgVal.replace(/[^0-9]/g, "");

ただし、前述のキーコード判定が効いているためこのままだとabc等は通常通り入力され、数値又はdel,bs押下時にフォーマットされます。

これを回避するため、カンマ編集処理が動くのは数値編集時のみじゃなくて、文字等が入力された場合にも動くようにします。

こんな感じになりました。

$("#target-text").bind("keyup", function(e) {
  var input, newPosition, newVal, orgLength, orgVal, position, val;
  orgVal = $(this).val();
  val = orgVal.replace(/[^0-9]/g, "");
  if ((e.keyCode >= 48 && e.keyCode <= 57) || e.keyCode 

===

 8 || e.keyCode 

===

 46 || /([^0-9,]|,,)/.test(orgVal)) {
    input = $(this).get(0);
    position = input.selectionStart;
    orgLength = orgVal.length;
    newVal = val.replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
    $(this).val(newVal);
    newPosition = position + newVal.length - orgLength;
    input.selectionStart = newPosition;
    input.selectionEnd = newPosition;
  }
});

実際の動作は以下の入力欄で。

数値・DEL・BSが押された場合とテキストに数値と2個以上のカンマがある場合にカンマ編集処理しています。

“カンマ2個以上”のところは強引な気がするのでもっといい方法あるかも。

また、この方法だと入力後に編集しているため入力文字がちらつきます。これを回避したい場合は実際に文字が挿入される前にキャプチャする必要があるため、keyupじゃなくてkeydownを使ってどうにかする必要があり、ここまで書いといてなんですが、

「失敗したなーーーー!!!」

というきがしています!

URABLO
広告
Index
広告