30

私のプロジェクトでは、キャレットのオフセット位置をtextareaピクセル単位で取得しようとしています。これはできますか?

ここで質問する前に、多くのリンク、特にティム ダウンのリンクを調べましたが、IE8+、Chrome、および Firefox で機能するソリューションを見つけることができませんでした。Tim Down がこれに取り組んでいるようです。

私が見つけた他の いくつかのリンクには、キャレット位置の上部オフセットが見つからないなど、多くの問題があります。

textareaキャレットのオフセット位置に基づいて配置することで、オートコンプリートの提案ボックスを内部に表示したいため、キャレットのオフセット位置を取得しようとしています。

PS:contenteditable div aに関連するコードをたくさん書いたので、 a を使用できませんtextarea

4

5 に答える 5

18

別の (不可視の) 要素を作成し、最初からカーソル位置までのテキストエリア コンテンツで埋めることができます。Textarea と「クローン」には、一致する CSS (フォント プロパティ、パディング/マージン/ボーダーと幅) が必要です。次に、これらの要素を互いに積み重ねます。

実際の例から始めて、コードを見てみましょう: http://jsfiddle.net/g7rBk/

更新された Fiddle ( IE8 修正あり)

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 のサイズと一致するように設定する必要があります。

于 2013-05-15T09:40:57.213 に答える
2

動作例の JsFiddle: http://jsfiddle.net/42zHC/2/

基本的に、幅に収まる列の数を計算します (モノスペースになるため)。スクロールバーが常にそこにあるように強制する必要があります。そうしないと、計算がオフになります。次に、幅に収まる列の数を割り、1 文字あたりの x オフセットを取得します。次に、テキストエリアに行の高さを設定します。連続する文字数がわかっているので、それを文字数で割り、行番号を取得します。行の高さで、y オフセットが得られます。次に、textarea の scrollTop を取得し、それを差し引いて、スクロールバーの使用を開始すると、正しい位置に表示されるようにします。

Javascript:

$(document).ready(function () {
  var cols = document.getElementById('t').cols;
  var width = document.getElementById('t').clientWidth;
  var height = $('textarea').css('line-height');
  var pos = $('textarea').position();
  $('#t').on('keyup', function () {
    el = document.getElementById("t");
    if (el.selectionStart) { 
        selection = el.selectionStart; 
      } else if (document.selection) { 
        el.focus(); 
        var r = document.selection.createRange(); 
        if (r == null) { 
           selection = 0; 
        } 
        var re = el.createTextRange(), 
        rc = re.duplicate(); 
        re.moveToBookmark(r.getBookmark()); 
        rc.setEndPoint('EndToStart', re); 
        selection = rc.text.length; 
      } else { selection = 0 }
    var row = Math.floor((selection-1) / cols);
    var col = (selection - (row * cols));
    var x = Math.floor((col*(width/cols)));
    var y = (parseInt(height)*row);
    $('span').html("row: " + row + "<br>columns" + col + "<br>width: " + width + "<br>x: " + x +"px<br>y: " + y +"px<br>Scrolltop: "+$(this).scrollTop()).css('top',pos.top+y-$(this).scrollTop()).css('left',pos.left+x+10);
  });
});

HTML:

<textarea id="t"></textarea>
<br>
<span id="tooltip" style="background:yellow"></span>

CSS:

textarea {
  height: 80px;
  line-height: 12px;
  overflow-y:scroll;
}
span {
  position: absolute;
}
于 2013-05-16T06:48:54.867 に答える
-1

私は似たようなものを得ることができなかったので、私の解決策はテキストエリアでキャレットの文字位置を見つけ、現在の段落を切り取り、これをテキストエリアの隣に表示することでした。

オフセットを使用して、編集可能なテキストのこのビューに偽のカーソル (、、幅 1 ピクセル) を配置しましdivdisplay:inlineborder-left: 1px solid black

このようにして、効果の結果を表示できる視覚的なフィードバック領域を作成できます (回答を書くときにスタックオーバーフローが行うのとまったく同じように)。

于 2013-04-25T12:42:15.413 に答える