Hatena::ブログ(Diary)

Webと文字

よければ、はてブしてください。( ´∀`) George.Nagaoka@gmail.com

JavaScript IME:海外からブラウザで日本語を変換 新URL 旧URL:
多言語入力ブックマークレット:ブラウザでロシア語、中国語、アラビア語・・・
【軍曹が】携帯電話開発の現状【語る】をAA化した
AAのデータベース
趣味のページ

2008-01-10

caret.js v0.21

1.対応ブラウザ

2.注意点

  • Prototype.jsが必要です。
  • Opera9.25(安定版)ではscrollTopの取得が出来ないので使えない。

3.使い方

$で取れるtextareaのメソッドとして登録してあるのでtextareaElementは$で取る必要があります。

var xy = $(textarea).getCaretXY();

4.ダウンロード

http://www28095u.sakura.ne.jp/caret/0.21/caret.js

5.サンプル

http://www28095u.sakura.ne.jp/caret/0.21/caret.html

2008-01-06

caret.js v0.2

1.0.2

prototype.jsを使ってtextareaエレメントのメソッドにしました。

var pos = $(textareaElement).getCaretPos();

2.バグ

Operaでtextareaのscrolltopの値が取得できません。なのでtextarea内でスクロールされるとずれてしまいます。解決策はないようです。(参考資料4)

枠線の処理はしていません。(参考資料5)

3.サンプル

caret.js実験 http://www28095u.sakura.ne.jp/caret/0.2/caret.html

4.参考資料

  1. Prototype JavaScript framework: Element.addMethods
  2. prototype.jsを読み解く:第6回 Prototypeライブラリ(1609〜2051行目)|gihyo.jp … 技術評論社
  3. Position.cumulativeOffset - エレメントのオフセット位置を求める - prototype.jsリファレンス
  4. Opera の scrollTop のバグ - m2O - チーム俺等
  5. offsetTop/offsetLeft/offsetParentの闇 - Backstage of theater.js

2007-12-28

テキストエリア内のキャレット処理関数

1.caret.js

参考資料のサイトを参考にしてテキストエリアのキャレットの処理に必要な関数をまとめました。IE6、Opera9.25、FireFox2.0で動作確認を取りました。それ以外のブラウザではおそらく動きません。またキャレットの座標を取得する関数はずれることがあります。


function getCaretPos(textarea){
  if(Prototype.Browser.IE) {
    var sel=document.selection.createRange();
    var sel_length = sel.text.length;
    var r=textarea.createTextRange();
    var all=r.text.length;
    r.moveToPoint(sel.offsetLeft,sel.offsetTop);
    r.moveEnd("textedit");
    var end_length=r.text.length;
    var start_length=all-end_length;
	 return start_length;
  }else if(Prototype.Browser.Opera || Prototype.Browser.Gecko){
    return textarea.selectionStart;
  }
}
//キャレット位置を返す
//数字
//prototype.jsが必要
//(IE/Opera/FireFox)

function atachCaretPos(textarea,ln){
  if(Prototype.Browser.IE){
    var e=textarea.createTextRange();
    var tx=textarea.value.substr(0, ln);
    var pl=tx.split(/\n/);
    e.collapse(true);
    e.moveStart("character",ln-pl.length+1);
    e.text=e.text+"";
    e.collapse(false);
    e.select();
  } else if(Prototype.Browser.Opera || Prototype.Browser.Gecko){
    textarea.setSelectionRange(ln, ln);
  }
  textarea.focus();
}
//キャレット位置を引数の位置に移す
//数字
//prototype.jsが必要
//(IE/Opera/FireFox)

function nowCaretPosPutWord(textarea,word){
    if(Prototype.Browser.IE){
      var ieRegion = document.selection.createRange();
      ieRegion.text = word;
      ieRegion.select();
    }else if(Prototype.Browser.Opera || Prototype.Browser.Gecko){
      var str=textarea.value;
	  var start_length = getCaretPos(textarea);
      var click_s=str.substr(0, start_length);
      var click_e=str.substr(start_length, textarea.value.length);
      textarea.value = click_s + word + click_e;
      atachCaretPos(textarea,start_length+word.length);
    }
    return false;
}
//現在のキャレット位置に指定の語句を追加する。getCaretPos関数、atachCaretPos関数が必要。
//数字
//prototype.jsが必要
//(IE/Opera/FireFox)

function setCaretPosPurWord(textarea,word,pos){
	var s = getCaretPos(textarea);
	atachCaretPos(textarea,pos);
	nowCaretPosPutWord(textarea,word);
	atachCaretPos(textarea,s);
}
//指定のキャレット位置に指定の文字を追加する。
//数字
//getCaretPos関数、atachCaretPos関数が必要。
//prototype.jsが必要
//(IE/Opera/FireFox)

function hogehoge(textarea,op){
    if(Prototype.Browser.IE){
		var hoge = function(){
			var caretPos = document.selection.createRange();
			var y =  caretPos.offsetTop + document.documentElement.scrollTop;
			var x =  caretPos.offsetLeft + document.documentElement.scrollLeft;
			return new Array(x,y);
		}
		return hoge;
	}else if(Prototype.Browser.Opera || Prototype.Browser.Gecko){
	
		var elmClone = document.createElement('pre');
		var elmCursor = document.createElement('span');
		elmCursor.innerHTML = '|';
		var flag=false;
		
		var copyProps = [
			'width', 'height',
			'padding-left', 'padding-right', 'padding-top', 'padding-bottom', 
			'border-left-style', 'border-right-style','border-top-style','border-bottom-style', 
			'border-left-width', 'border-right-width','border-top-width','border-bottom-width', 
			'font-family', 'font-size','line-height', 'letter-spacing', 'word-spacing'
		];
		
		var setElmStyle = function (textarea){
			var getProps = new Array();
			for(var i=0;copyProps.length>i;i++){
				getProps[copyProps[i]] = textarea.getStyle(copyProps[i]);
	    	}
			elmClone.setStyle(getProps);
			
			//手動で設定
			elmClone.style.left = textarea.offsetLeft + 'px';
			elmClone.style.top = textarea.offsetTop + 'px';
			elmClone.style.width = textarea.offsetWidth;
			elmClone.style.height = textarea.offsetHeight;
			elmClone.style.visibility="hidden"; 
			elmClone.style.position = "absolute"; 
			elmClone.scrollLeft = textarea.scrollLeft;
			elmClone.scrollTop = textarea.scrollTop;
			//ブラウザ別処理
			if(Prototype.Browser.Gecko){
				elmClone.style.whiteSpace = "-moz-pre-wrap";
			}else if(Prototype.Browser.Opera){
				elmClone.style.whiteSpace = "-o-pre-wrap";
				elmClone.style.lineHeight = "15px";
			}else{
				elmClone.style.whiteSpace = "pre-wrap";
			}
		}
		
		var constructor = function (textarea){
			setElmStyle(textarea);
			document.getElementsByTagName("body").item(0).appendChild(elmClone);
		}
		
		var hoge = function(textarea,op){
			if(!flag || op){
				constructor(textarea);
				flag = true;
			}
			var selectionEnd = textarea.selectionEnd;
			var value = textarea.value;
			elmClone.innerHTML = '';
			elmClone.appendChild(document.createTextNode(value.substr(0,selectionEnd)));
			elmClone.appendChild(elmCursor);
			var x = textarea.offsetLeft + elmCursor.offsetLeft +2 - textarea.scrollLeft;
			var y = textarea.offsetTop + elmCursor.offsetTop -2 - textarea.scrollTop;
			return new Array(x,y);
		}
		
		return hoge;
	}
}
var getCaretXY = hogehoge();
//現在のキャレット座標を返す。
//getCaretPos関数が必要。
//配列を返す。Array[0] = x,Array[1] = y。単位はピクセル。
//prototype.jsが必要
//第二引数 0:最初の一回のみ、1:毎回計算する。
//(IE/Opera/FireFox)

http://www3.pf-x.net/~project-x/pj1/caret/caret.js

2.参考資料

  1. 特にタイトル無し
  2. textarea cursor (brasil)
  3. prototype.js 1.5.0を読む (65) - Element拡張 getStyle、setStyleメソッド - Marzzuo.blog [マルゾ・ブログ]
  4. PREで自動改行する方法 - javascript 覚え書き日記 - g:javascript
  5. document.createElement - 指定タグでのエレメント作成 - JavaScriptリファレンス

2007-12-26

テキストエリア内のキャレット座標を取得する

1.IE(var6)+Sleipnir(var2.6.1)

参考資料1の通りである。以下のコードで座標位置を取得できる。

	var caretPos = document.selection.createRange();
	y =  (caretPos.offsetTop + document.documentElement.scrollTop); 
	x =  (caretPos.offsetLeft + document.documentElement.scrollLeft);

サンプル:http://www28095u.sakura.ne.jp/textareacaret/e.html

2.Mozilla FireFox (var2.0.0.11)

参考資料2の方法を使う。

  1. pre要素の作成。あらかじめ作成するか、createElementメソッドで作成する。
  2. テキストエリアのスタイルをgetComputedStyleメソッドで取得し、1で作成したpre要素のスタイルにコピーする。
  3. テキストエリアのカーソル間での文字列をコピーしてpre要素内に追加する。
  4. span要素を作成して中に適当な文字を入れてpre要素に追加する。
  5. 元のテキストエリアのoffsetとpre要素内のspan要素のoffsetを加算して座標が求まる。

参考資料2と違うところは

  1. getComputedStyleの再定義の箇所を削除した。
    • Mozilla FireFoxにはすでにgetComputedStyleが定義されている。(参考資料3)
  2. pre要素のスタイルのvisibilityの値をhiddenにして要素全体を不可視にした。
    • pre要素は座標計算用に作成したものなので表示する必要はない。
  3. pre要素のスタイルのpositionの値をabsoluteにして要素全体をテキストエリアと重ねた。
    • 2で不可視にしてもウィンドウからはみ出すと自動的にスクロールバーが表示されてしまう。それを防ぐために要素を重ねる必要がある。
  4. カーソル位置の計算を行う関数(getSelection)を削除した。
    • Mozillaでは簡単に取得できる。(参考資料4)

また、座標位置は単純に加算で求めているため、テキストエリアが何かの子要素の場合にはその親要素のオフセットも加算する必要がある。オフセットの値を求めるには参考資料7,8が詳しい。

サンプル:http://www28095u.sakura.ne.jp/textareacaret/e.html

3.Opera(var9.25)

Mozilla FireFoxとほぼ同じであるが、getComputedStyleを使って行間(line-height)をうまく取得することができない。そこでcopyPropsからline-heightを抜いて、手動で行間を設定してやる必要がある。以下のコードを追加する。

if(!elmClone.style.lineHeight){
	elmClone.style.lineHeight = "122%"; //15px
}else{
	elmClone.style.lineHeight = elmOriginal.style.lineHeight;
}

オフセットの値に注意するのはMozillaと同じである。

サンプル:http://www28095u.sakura.ne.jp/textareacaret/e.html

4.問題点

2、3に関しては以下の問題がある。

  • 自動改行に対応していない。
  • テキストエリア内でスクロールされるとずれる。

5.参考資料

  1. きまぐれ日記: Javascript でキャレットの位置を取得できる?
  2. textarea cursor (brasil)
  3. Gecko DOM Reference:Examples - MDC
  4. IE、テキストエリア内の選択範囲/キャレットの位置(文字数)を取得する - 実用
  5. 改行コード、pre、textarea - 実用
  6. Opera 9.5スナップショット(Build 9600/9603)更新履歴 (kuruman.org > Kuruman Memo)
  7. offsetTop/offsetLeft/offsetParentの闇 - Backstage of theater.js
  8. anything from here offsetLeft,offsetTop,offsetWidthそしてoffsetHeight──静的配置要素の絶対位置を確実に取得する方法について