web-dev-qa-db-ja.com

HTMLコンテンツを含むcontentEditable領域のキャレット(カーソル)位置を取得します

ContentEditable要素(p、divなど)があり、その中にキャレット(カーソル)の位置を取得したいです。私は通常、次のコードでそれを達成できます。

var position = window.getSelection().getRangeAt(0).startOffset;

要素にテキストのみが含まれている場合、これは正常に機能します。ただし、要素にHTML形式が含まれている場合、返される位置は、含まれているHTML要素内のキャレット位置に相対的です。

ContentEditable要素のコンテンツがこれだと仮定しましょう:

AB<b>CD</b>EF

キャレットが<b></b>内にある場合、CとDの間で、上記のコードで返される位置は3ではなく1です(contentEditable要素のコンテンツの先頭から数えられます)

誰でもこれに対する解決策を思いつくことができますか?

56
Frodik

更新

IE <9:でも動作するこの簡単なバージョンを作成しました。

https://stackoverflow.com/a/4812022/961

古い答え

これは実際には、ドキュメント全体のテキスト内の文字オフセットよりも有用な結果です。DOM範囲のstartOffsetプロパティ(window.getSelection().getRangeAt()が返すもの)は、その相対オフセットです。 startContainerプロパティ(ちなみに、必ずしもテキストノードであるとは限りません)。ただし、文字オフセットが本当に必要な場合は、これを行う関数を次に示します。

以下に実際の例を示します。 http://jsfiddle.net/timdown/2YcaX/

関数は次のとおりです。

function getCharacterOffsetWithin(range, node) {
    var treeWalker = document.createTreeWalker(
        node,
        NodeFilter.SHOW_TEXT,
        function(node) {
            var nodeRange = document.createRange();
            nodeRange.selectNode(node);
            return nodeRange.compareBoundaryPoints(Range.END_TO_END, range) < 1 ?
                NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
        },
        false
    );

    var charCount = 0;
    while (treeWalker.nextNode()) {
        charCount += treeWalker.currentNode.length;
    }
    if (range.startContainer.nodeType == 3) {
        charCount += range.startOffset;
    }
    return charCount;
}
51
Tim Down

これは非常に古い投稿ですが、まだGoogleで検索した最初の結果の1つであるため、まだ便利です。これは、htmlタグと改行も考慮して正しい位置を取得するために機能します(Firefoxでテスト済み):

function getCaretPosition (node) {
    var range = window.getSelection().getRangeAt(0),
        preCaretRange = range.cloneRange(),
        caretPosition,
        tmp = document.createElement("div");

    preCaretRange.selectNodeContents(node);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    tmp.appendChild(preCaretRange.cloneContents());
    caretPosition = tmp.innerHTML.length;
    return caretPosition;
}

実際のHTMLを取得するためにcloneContents機能を使用し、HTMLの長さを取得するために一時的なdivにdocumentfragmentを追加します。

6
Andrea

要素を挿入する場合は、次のようなことを試してみてください。

// Get range
var range = document.caretRangeFromPoint(event.clientX, event.clientY);
if (range)
  range.insertNode(elementWhichYouWantToAddToContentEditable);
1
Antti