Comment définir la position du curseur dans l'élément contenteditable (div)?

J'ai ce HTML simple comme exemple:

<div id="editable" contenteditable="true">
  text text text<br>
  text text text<br>
  text text text<br>
</div>
<button id="button">focus</button>

Je veux une chose simple-quand je clique sur le bouton, je veux placer le curseur (curseur) dans un endroit spécifique dans le div modifiable. De la recherche sur le web, j'ai ce JS attaché au clic de bouton, mais cela ne fonctionne pas (FF, Chrome):

var range = document.createRange();
var myDiv = document.getElementById("editable");
range.setStart(myDiv, 5);
range.setEnd(myDiv, 5);

Est - il possible de définir manuellement la position du curseur comme ceci ?

142
demandé sur Frodik 2011-06-06 11:50:33

5 réponses

Dans la plupart des navigateurs, vous avez besoin de la Range et Selection objets. Vous spécifiez chacune des limites de sélection en tant que nœud et un décalage dans ce nœud. Par exemple, pour définir le curseur sur le cinquième caractère de la deuxième ligne de texte, procédez comme suit:

var el = document.getElementById("editable");
var range = document.createRange();
var sel = window.getSelection();
range.setStart(el.childNodes[2], 5);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);

IE

Jsfiddle exemple: http://jsfiddle.net/timdown/vXnCM/

194
répondu Tim Down 2013-07-14 20:08:32

La plupart des réponses que vous trouvez sur le positionnement du curseur contenteditable sont assez simplistes en ce sens qu'elles ne répondent qu'aux entrées avec du texte vanillé. Une fois que vous utilisez des éléments html dans le conteneur, le texte entré est divisé en nœuds et distribué généreusement dans une arborescence.

Pour définir la position du curseur, j'ai cette fonction qui boucle tous les nœuds de texte enfant dans le nœud fourni et définit une plage du début du nœud initial aux caractères .Compter caractère:

function createRange(node, chars, range) {
    if (!range) {
        range = document.createRange()
        range.selectNode(node);
        range.setStart(node, 0);
    }

    if (chars.count === 0) {
        range.setEnd(node, chars.count);
    } else if (node && chars.count >0) {
        if (node.nodeType === Node.TEXT_NODE) {
            if (node.textContent.length < chars.count) {
                chars.count -= node.textContent.length;
            } else {
                range.setEnd(node, chars.count);
                chars.count = 0;
            }
        } else {
           for (var lp = 0; lp < node.childNodes.length; lp++) {
                range = createRange(node.childNodes[lp], chars, range);

                if (chars.count === 0) {
                    break;
                }
            }
        }
    } 

    return range;
};

J'appelle alors la routine avec cette fonction:

function setCurrentCursorPosition(chars) {
    if (chars >= 0) {
        var selection = window.getSelection();

        range = createRange(document.getElementById("test").parentNode, { count: chars });

        if (range) {
            range.collapse(false);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    }
};

La plage.effondrement(faux) place le curseur à la fin de la plage. Je l'ai testé avec les dernières versions de Chrome, IE, Mozilla et Opera et ils fonctionnent tous très bien.

PS. Si quelqu'un est intéressé, j'obtiens la position actuelle du curseur en utilisant ce code:

function isChildOf(node, parentId) {
    while (node !== null) {
        if (node.id === parentId) {
            return true;
        }
        node = node.parentNode;
    }

    return false;
};

function getCurrentCursorPosition(parentId) {
    var selection = window.getSelection(),
        charCount = -1,
        node;

    if (selection.focusNode) {
        if (isChildOf(selection.focusNode, parentId)) {
            node = selection.focusNode; 
            charCount = selection.focusOffset;

            while (node) {
                if (node.id === parentId) {
                    break;
                }

                if (node.previousSibling) {
                    node = node.previousSibling;
                    charCount += node.textContent.length;
                } else {
                     node = node.parentNode;
                     if (node === null) {
                         break
                     }
                }
           }
      }
   }

    return charCount;
};

Le code fait le contraire de la fonction set - il obtient la fenêtre actuelle.getSelection ().focusNode et focusOffset et comptes en arrière tous les caractères de texte rencontrés jusqu'à ce qu'il frappe un nœud parent avec l'id de containerId. La fonction isChildOf vérifie juste avant d'exécuter que le nœud fourni est en fait un enfant du ParentID fourni.

Le code devrait fonctionner directement sans changement, mais je viens de le prendre à partir d'un plugin jQuery que j'ai développé, donc j'ai piraté quelques c'est - Faites-moi savoir si quelque chose ne fonctionne pas!

31
répondu Liam 2016-12-12 16:44:32

Il est très difficile de placer le curseur dans la bonne position lorsque vous avez un élément advance comme (p) (span) etc.Le but est d'obtenir (texte de l'objet):

     <div id="editable" contenteditable="true">dddddddddddddddddddddddddddd<p>dd</p>psss<p>dd</p><p>dd</p>
    <p>text text text</p>
     </div>
    <p id='we'></p>
    <button onclick="set_mouse()">focus</button>
     <script>

function set_mouse() {
    var as = document.getElementById("editable");
    el=as.childNodes[1].childNodes[0];//goal is to get ('we') id to write (object Text) because it work only in object text
  var range = document.createRange();
     var sel = window.getSelection();
range.setStart(el, 1);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);

document.getElementById("we").innerHTML=el;// see out put of we id
}
</script>
2
répondu Jalaluddin Rumi 2016-08-26 08:20:47

Si vous ne souhaitez pas utiliser jQuery, vous pouvez essayer cette approche:

public setCaretPosition() {
    const editableDiv = document.getElementById('contenteditablediv');
    const lastLine = this.input.nativeElement.innerHTML.replace(/.*?(<br>)/g, '');
    const selection = window.getSelection();
    selection.collapse(editableDiv.childNodes[editableDiv.childNodes.length - 1], lastLine.length);
}

editableDiv vous élément modifiable, n'oubliez pas de définir un id pour cela. Ensuite, vous devez obtenir votre innerHTML de l'élément et couper toutes les lignes de frein. Et il suffit de définir l'effondrement avec les arguments suivants.

0
répondu Volodymyr Khmil 2018-05-05 05:38:38

J'écris un surligneur de syntaxe (et un éditeur de code de base), et j'avais besoin de savoir comment taper automatiquement un seul caractère de citation et déplacer le curseur (comme beaucoup d'éditeurs de code de nos jours).

Voici un extrait de ma solution, grâce à beaucoup d'aide de ce fil, les documents MDN, et beaucoup de console MOZ regarder..

//onKeyPress event

if (evt.key === "\"") {
    let sel = window.getSelection();
    let offset = sel.focusOffset;
    let focus = sel.focusNode;

    focus.textContent += "\""; //setting div's innerText directly creates new
    //nodes, which invalidate our selections, so we modify the focusNode directly

    let range = document.createRange();
    range.selectNode(focus);
    range.setStart(focus, offset);

    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
}

//end onKeyPress event

Ceci est dans un élément div contenteditable

Je laisse ceci ici en remerciement, réalisant qu'il y a déjà une réponse acceptée.

0
répondu Jonathan Crowder 2018-10-09 07:52:55