別の (不可視の) 要素を作成し、最初からカーソル位置までのテキストエリア コンテンツで埋めることができます。Textarea と「クローン」には、一致する CSS (フォント プロパティ、パディング/マージン/ボーダーと幅) が必要です。次に、これらの要素を互いに積み重ねます。
実際の例から始めて、コードを見てみましょう: http://jsfiddle.net/g7rBk/
HTML:
<textarea id="input"></textarea>
<div id="output"><span></span></div>
<div id="xy"></div>
テキストエリアは自明です。出力は、テキスト コンテンツを渡して対策を講じる非表示の要素です。重要なのは、インライン要素を使用することです。「xy」div は、テスト目的の指標にすぎません。
CSS:
/* identical styling to match the dimensions and position of textarea and its "clone"
*/
#input, #output {
position:absolute;
top:0;
left:0;
font:14px/1 monospace;
padding:5px;
border:1px solid #999;
white-space:pre;
margin:0;
background:transparent;
width:300px;
max-width:300px;
}
/* make sure the textarea isn't obscured by clone */
#input {
z-index:2;
min-height:200px;
}
#output {
border-color:transparent;
}
/* hide the span visually using opacity (not display:none), so it's still measurable; make it break long words inside like textarea does. */
#output span {
opacity:0;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* the cursor position indicator */
#xy {
position:absolute;
width:4px;
height:4px;
background:#f00;
}
JavaScript:
/* get references to DOM nodes we'll use */
var input = document.getElementById('input'),
output = document.getElementById('output').firstChild,
position = document.getElementById('position'),
/* And finally, here it goes: */
update = function(){
/* Fill the clone with textarea content from start to the position of the caret. You may need to expand here to support older IE [1]. The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line.
*/
output.innerHTML = input.value.substr( 0, input.selectionStart ).replace(/\n$/,"\n\001");
/* the fun part!
We use an inline element, so getClientRects[2] will return a collection of rectangles wrapping each line of text.
We only need the position of the last rectangle.
*/
var rects = output.getClientRects(),
lastRect = rects[ rects.length - 1 ],
top = lastRect.top - input.scrollTop,
left = lastRect.left+lastRect.width;
/* position the little div and see if it matches caret position :) */
xy.style.cssText = "top: "+top+"px;left: "+left+"px";
}
[1]テキストエリアのキャレット位置、先頭からの文字数
[2] https://developer.mozilla.org/en/docs/DOM/element.getClientRects
編集: この例は、固定幅のテキストエリアでのみ機能します。ユーザーがサイズ変更可能なテキストエリアで機能させるには、resize イベントにイベント リスナーを追加し、#output のサイズを新しい #input のサイズと一致するように設定する必要があります。