not good but great

プログラミング、アート、映画・本の感想について書きます。

テキストエリアのサイズを可変する時のバグ

f:id:naoyashiga:20130708013459p:plain

6月にエントリーシートの下書き作成用エディタをつくった。
ES Draft
http://naoyashiga.github.io/ES_Draft/

行数カウンターを設置しているが、それの数え方がうまくいかない。行数をカウントして、テキストエリアのサイズを可変にするようにしていた。しかしうまく可変されないバグがあった。

修正前

当初は次のようにしていた。

行数 = テキストエリアの文字数 / テキストエリアのカラム数(1行に入力できる文字数)

問題は2つあった。

問題1
改行文字を考慮していなかった。改行ばかりのテキストを入力すると、行数が少なくカウントされて、可変にならない。
問題2
テキストエリアのカラム数(1行に入力できる文字数)が一定ではない。ひらがな、英語で文字数が変わるのでどうしようもない。だから行数を正しくカウントできない。

 //テキストエリアの縦を可変にする  
$("textarea").bind("keyup",function(){
    var id = $(this).attr("id");
    var cur = this;
    //テキストエリアの文字数を計算 改行文字を消去
    var value = $(this).val().replace(/\n/g,"");
    //編集中のテキストエリアの行数を計算
    var row = Math.max(Math.floor(value.length / cur.cols),1);

    //行数の初期値を設定
    if(id == "title")var init_row = TITLE_ROWS;
    else var init_row = DRAFT_ROWS;

    if(row > init_row) $(this).attr("rows",row);
    else $(this).attr("rows",init_row);
});

修正後

今回は問題1だけを直した。 改行文字の数をカウントして、テキストエリアの行数の初期値を超えたら、行数を足すことにした。

ここで修正前のコードを見る。

//タイトル行数の初期値 String型
var TITLE_ROWS = $("#title").attr("rows");
//ドラフト行数の初期値 String型
var DRAFT_ROWS = $("#draft").attr("rows");

//~途中省略~
if(row > init_row) $(this).attr("rows",row);
else $(this).attr("rows",init_row);

初期値それぞれは、テキストエリアの属性「rows」を取得してるのでStringである。その語、テキストエリアを可変にするために、「rows」を設定し直している。jQueryにおいてattr()の第2引数はStringで正しく動いていた。

修正後のコードは次のようにした。あとで改行文字数を足すかもしれないので、属性「rows」の値をIntegerで取得した。

//タイトル行数の初期値 Integer
var TITLE_ROWS = parseInt($("#title").attr("rows"));
//ドラフト行数の初期値 Integer
var DRAFT_ROWS = parseInt($("#draft").attr("rows"));

//~途中省略~
if(row + len > init_row) $(this).attr("rows",row + len);
else $(this).attr("rows",init_row);

これをStringとIntegerを混合させて、セットすると次のようになる。ここからjQueryのattr()の第2引数の計算で使われるIntegerは自動的にStringに型変換された後、計算されるようだ。

var str = "100";
var num = 1;
$(this).attr("rows",str + num);
console.log($(this).attr("rows"));
//出力結果:1001

ちなみにStringをIntegerに直す方法として「+」をつけるやり方があるみたいだ。
参考:JavaScript イディオム集http://nmi.jp/archives/488

var str = "100";
var num = 1;
$(this).attr("rows",+str + num);//str→+str
console.log($(this).attr("rows"));
//出力結果:101

最終的に修正したコードは次のようになった。

//テキストエリアの縦を可変にする  
    $("textarea").bind("keyup",function(){
        var id = $(this).attr("id");
        var cur = this;
        //改行文字数
        var len = 0;

        if($(this).val().match(/\n/g)){
            //改行の個数+1行目
            len = $(this).val().match(/\n/g).length + 1;
        }
        
        //テキストエリアの文字数を計算
        var value = $(this).val().replace(/\n/g,"");
        //編集中のテキストエリアの行数を計算
        var row = Math.max(Math.floor(value.length / cur.cols),1);

        //行数の初期値を設定
        if(id == "title")var init_row = TITLE_ROWS;
        else if(id == "draft")var init_row = DRAFT_ROWS;

        if(row + len > init_row) $(this).attr("rows",row + len);
        else $(this).attr("rows",init_row);
    });

結果

問題1は解決できたので、より正しく可変されるようになった。しかし問題2が解決していないために、ソースコードのような改行ばかりのテキストをペーストした場合、テキストエリアが必要以上に長くなってしまう。どうにか解決できないものか。