テキストエディタで検索をやるべくSelectionを調べた。
JavaScriptでのSelectionの扱いは決っていないらしくIEとFirefoxで全然違う (;_;)

Firefox は極めてシンプル。
- textarea.selectionStart と textarea.selectionEnd プロパティに現在の選択位置が入る。
- textarea.setSelectionRange(s,e) で選択位置変更。
わっかりやすーい。

問題はIE、
- 選択範囲を持つクラス TextRange が有る。
- 現在の選択位置は document.selection.createRange() で取得。
- 選択位置の設定は TextRange.move() で移動させて TextRange.select() を呼ぶ。

問題なのは「textarea.value に対する絶対位置は取得/設定できない」仕様だと言うこと。
抽象化といえば聞こえは良いがめちゃくちゃ使いづらい。
検索に関しては TextRange.findText() 関数があるのでこれに任せる。

Firefoxにも問題があって選択位置を設定してもそこにスクロールしてくれない。
逆算して自前でスクロールさせる必要がある。


で、実際の検索関数がこんな感じ。ほとんど共通部分が無い...

node.find = function(_this, direct) {
var text = JSCP.getElementByClass(this, "JSCP_Editor_Text");
var find = JSCP.getElementByClass(this, "JSCP_Editor_Find");

if (text.createTextRange) { // for IE
var range = this.getTextRange(text);
if (range.text == find.value) {
var position = (direct>0) ? "StartToEnd" : "EndToStart";
range.setEndPoint(position, range);
}
var flag = 0x4;
// 0x0=match partial words, 0x1=match backwards,
// 0x2=match whole words only, 0x4=match case.
var isFound = range.findText(find.value ,direct ,flag);
if (isFound) range.select();
return isFound;
} else if (text.setSelectionRange) {
if (direct > 0) {
var pos = text.value.indexOf(find.value, text.selectionEnd);
if (pos < 0) pos = text.value.indexOf(find.value, 0);
} else {
var pos = text.value.lastIndexOf(find.value, text.selectionStart-1);
if (pos < 0) pos = text.value.lastIndexOf(find.value, text.value.length);
}
if (pos < 0) return false;

text.setSelectionRange(pos, pos + find.value.length);
var lineNum = text.value.substr(0,pos).split("\n").length;
text.scrollTop = (lineNum-1) * this.LINE_HEIGHT;
text.focus();
return true;
}
return false;
}
node.getTextRange = function(text) { // for IE
if (!text.textRange) text.textRange = text.createTextRange();
return text.textRange;
}



Subject: TEXTAREAで置換
Content-type: lovelog/text
Tags: JavaScript
Date: 2009/12/08
Public: yes

検索ができたら当然、置換もと言うことで調べた。

今度は逆にIEはめちゃくちゃ楽ちん。
TextRange.text に置換文字列を入れるだけ。

Firefox は textarea.value を2つに分けて置換文字列を挟んで書き戻す。
話はシンプルだがコードにするとなんか汚い。

で、関数にするとこんな感じ。やっぱり、ほとんど共通部分が無い..


node.replace = function(_this, direct) {
if (!this.find(_this, direct)) return false;
var text = JSCP.getElementByClass(this, "JSCP_Editor_Text");
var replace = JSCP.getElementByClass(this, "JSCP_Editor_Replace");

if (text.createTextRange) { // for IE
var range = this.getTextRange(text);
range.text = replace.value;
} else if (text.setSelectionRange) {
var start = text.selectionStart;
var part0 = text.value.substr(0, text.selectionStart);
text.value = part0
+ replace.value
+ text.value.substr(text.selectionEnd);
var lineNum = part0.split("\n").length;
text.scrollTop = (lineNum-1) * 16; // line-height:16px;
text.setSelectionRange(start, start+replace.value.length);
}
return true;
}



Subject: TEXTAREAでTAB入力
Content-type: lovelog/text
Tags: JavaScript
Date: 2009/12/08
Public: yes

も一つ TEXTAREA で気に入らないのが TAB が入力できない事。
仕様的には TAB は表示も保証されないので仕方が無いけど現実問題として必要なんだよね。

ググってみたらやたらややこしいコードが出て来たんだけど自分で書いてみたら割とシンプルにできた。
やってる事は keydown イベントを拾って置換のテクでカーソル位置にTABを挿入するだけ。
但し、イベントの伝播を止め無いとフォーカスが移動しちゃんうんで注意。

関数はこんな感じ。

// Ex. <textarea onkeydown="node.keydown(this, event)" >
node.keydown = function(text, ev) {
if (ev.keyCode != 9) return; // TAB only.
if (ev.preventDefault) {
ev.preventDefault();
ev.stopPropagation();
}
ev.returnValue = false;

if (text.createTextRange) { // for IE
var range = document.selection.createRange();
range.text = "\t";
} else if (text.setSelectionRange) {
var top = text.scrollTop;
var start = text.selectionStart;
text.value = text.value.substr(0, text.selectionStart)
+ "\t" + text.value.substr(text.selectionEnd);
text.setSelectionRange(start+1,start+1);
text.scrollTop = top;
}
}