web-dev-qa-db-fra.com

Obtenir la position du curseur (curseur) dans le contenu Zone modifiable contenant du contenu HTML

J'ai un élément contentEditable (peut être p, div, ...) et je voudrais y placer un curseur (curseur). Je peux normalement y parvenir avec ce morceau de code:

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

Cela fonctionne bien alors que l'élément ne contient que du texte. Mais lorsque l'élément contient une mise en forme HTML, la position renvoyée est relative à la position du curseur dans l'élément HTML inclus.

Supposons que le contenu de l'élément contentEditable soit le suivant:

AB<b>CD</b>EF

Si le curseur est à l'intérieur <b></b>, disons entre C et D, la position retournée avec le code ci-dessus est 1 au lieu de 3 (compté à partir du début du contenu de l'élément contentEditable)

Quelqu'un peut-il trouver une solution à cela?

56
Frodik

MISE À JOUR

J'ai écrit une version plus simple de cela qui fonctionne également en IE <9:

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

Ancienne réponse

C'est en fait un résultat plus utile qu'un décalage de caractère dans le texte de tout le document: la propriété startOffset d'une plage DOM (qui est ce que window.getSelection().getRangeAt() renvoie) est un décalage par rapport à son startContainer propriété (qui n'est pas nécessairement toujours un nœud de texte, soit dit en passant). Cependant, si vous voulez vraiment un décalage de caractère, voici une fonction qui le fera.

Voici un exemple en direct: http://jsfiddle.net/timdown/2YcaX/

Voici la fonction:

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

Il s'agit d'un très ancien article, mais toujours l'un des premiers résultats de recherche sur Google, donc peut-être toujours utile. Cela fonctionne pour moi pour obtenir la bonne position en tenant compte des balises html et des nouvelles lignes également (testé sur 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;
}

Il utilise la fonctionnalité cloneContents afin d'obtenir le html réel et ajoute le documentfragment à un div temporaire afin d'obtenir la longueur html.

6
Andrea

Si vous souhaitez insérer un élément, vous pouvez essayer de faire quelque chose comme ceci:

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