简体   繁体   中英

Set Caret Position in 'contenteditable' div that has children

I have an HTML structure like this:

<div contenteditable="true">This is some plain, boring content.</div>

I also have this function that allows me to set the caret position to anywhere I want within the div:

// Move caret to a specific point in a DOM element
function SetCaretPosition(object, pos)
{
    // Get key data
    var el = object.get(0); // Strip inner object from jQuery object
    var range = document.createRange();
    var sel = window.getSelection();

    // Set the range of the DOM element
    range.setStart(el.childNodes[0], pos);
    range.collapse(true);

    // Set the selection point
    sel.removeAllRanges();
    sel.addRange(range);
}

This code works completely fine until I start adding child tags (span, b, i, u, strike, sup, sub) to the div eg

<div contenteditable="true">
    This is some <span class="fancy">plain</span>, boring content.
</div>

Things get more complicated when these child tags end up with child tags of their own eg

<div contenteditable="true">
    This is some <span class="fancy"><i>plain</i></span>, boring content.
</div>

Essentially, what happens, is that setStart throws an IndexSizeError when I try to SetCaretPosition to an index higher than the start of a child tag. setStart only works until it reaches the first child tag.

What I need, is for the SetCaretPosition function to handle an unknown number of these child tags (and potentially an unknown number of nested child tags) so that setting the position works in the same way it would if there were no tags.

So for both this:

<div contenteditable="true">This is some plain, boring content.</div>

and this:

<div contenteditable="true">
    This is <u>some</u> <span class="fancy"><i>plain</i></span>, boring content.
</div>

SetCaretPosition(div, 20); would place the caret before the 'b' in 'boring'.

What is the code I need? Many thanks!

So, I was experiencing the same issue and decided to write my own routine quickly, it walks through all the child nodes recursively and set the position. Note how this takes a DOM node as argument, not a jquery object as your original post does

// Move caret to a specific point in a DOM element
function SetCaretPosition(el, pos){

    // Loop through all child nodes
    for(var node of el.childNodes){
        if(node.nodeType == 3){ // we have a text node
            if(node.length >= pos){
                // finally add our range
                var range = document.createRange(),
                    sel = window.getSelection();
                range.setStart(node,pos);
                range.collapse(true);
                sel.removeAllRanges();
                sel.addRange(range);
                return -1; // we are done
            }else{
                pos -= node.length;
            }
        }else{
            pos = SetCaretPosition(node,pos);
            if(pos == -1){
                return -1; // no need to finish the for loop
            }
        }
    }
    return pos; // needed because of recursion stuff
}

I hope this'll help you!

If you are going to position the caret based on the character index of the content editable, the easiest way would be using modify method:

const position = 6;

const el = document.querySelector('#editable');
const selection = document.getSelection();

if (!selection || !el) return;

// Set the caret to the beggining
selection.collapse(el, 0);

// Move the caret to the position
for (let index = 0; index < position; index++) {
  selection.modify('move', 'forward', 'character');
}

No need to traverse the child nodes.

It only work for object Text childNodes(0).So you have to make it.Here is not so very standard code,but works.Goal is that (p) id of (we) will output object text.If it does then it might work.

<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)
  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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM