1

I have a contenteditable div and I need to move the caret to a position of a string but I just can't see how to do this. Does anyone have any suggestions?

<div id='mydiv' contenteditable='true'>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Sed eget libero sit <mark>amet</mark> and **peekabo** magna sagittis sagittis quis nec risus.
    Pellentesque feugiat pharetra purus id pharetra.
</div>

<script>
doFindStringAndMoveCaret('mydiv', 'peekabo');


function doFindStringAndMoveCaret(elem, searchFor) { 
    // Find searchFor string in the elem.innerHTML, and then move the 
    // cursor to the beginning of that text

    ??
}

Ideally plain old javascript (not jQuery)

Thanks Abe

user1432181
  • 918
  • 1
  • 9
  • 24
  • Does this answer your question? [How to set caret(cursor) position in contenteditable element (div)?](https://stackoverflow.com/questions/6249095/how-to-set-caretcursor-position-in-contenteditable-element-div) – shreyasm-dev Nov 06 '20 at 15:21
  • The suggestion talks about setting the caret position to a coded range, but it's the node searching/walking alongside the ranges that's just not connecting for me. – user1432181 Nov 06 '20 at 15:28
  • The `div` will have to be `contentEditable` to move the caret in it. – shreyasm-dev Nov 06 '20 at 15:36
  • Sorry - my mistake in the post - it is a contenteditable (I've now updated the sample) – user1432181 Nov 06 '20 at 15:41

3 Answers3

0

function doFindStringAndMoveCaret(elem, text) {
  function setCurrentCursorPosition(element, chars) {
    element.focus()
    if (chars >= 0) {
      var selection = window.getSelection();

      range = createRange(element.parentNode, {
        count: chars
      });

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

    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;
    }
  }

  setCurrentCursorPosition(elem, new RegExp(text).exec(elem.innerHTML).index - 1)
}

doFindStringAndMoveCaret(document.getElementById('mydiv'), 'peekabo')
<div id='mydiv' contenteditable='true'>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  Sed eget libero sit <mark>amet</mark> and **peekabo** magna sagittis sagittis quis nec risus.
  Pellentesque feugiat pharetra purus id pharetra.
</div>

This just finds the position of the string in the innerHTML of the div, subtracts 1 from that, and uses setCurrentCursorPosition to focus the div and move the cursor to there:

setCurrentCursorPosition(elem, new RegExp(text).exec(elem.innerHTML).index - 1)

The rest of the code in the function is modified and taken from https://stackoverflow.com/a/41034697/.

If you want to move the cursor to the start of the text, use

setCurrentCursorPosition(elem, new RegExp(text).exec(elem.innerHTML).index - text.length - 1)

instead.

function doFindStringAndMoveCaret(elem, text) {
  function setCurrentCursorPosition(element, chars) {
    element.focus()
    if (chars >= 0) {
      var selection = window.getSelection();

      range = createRange(element.parentNode, {
        count: chars
      });

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

    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;
    }
  }

  setCurrentCursorPosition(elem, new RegExp(text).exec(elem.innerHTML).index - text.length - 1)
}

doFindStringAndMoveCaret(document.getElementById('mydiv'), 'peekabo')
<div id='mydiv' contenteditable='true'>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  Sed eget libero sit <mark>amet</mark> and **peekabo** magna sagittis sagittis quis nec risus.
  Pellentesque feugiat pharetra purus id pharetra.
</div>
shreyasm-dev
  • 2,711
  • 5
  • 16
  • 34
  • Thanks GalaxyCat105, this is great... but testing it on https://jsfiddle.net/Abeeee/65upgj20/1/ with a little variance on the html it returns with the wrong position. – user1432181 Nov 06 '20 at 16:13
  • @user1432181 You need to pass the `p` element into the function instead of `mydiv`. It works if I remove the `p`. – shreyasm-dev Nov 06 '20 at 16:15
  • @user1432181 Actually, it doesn't work even if I pass in the `p` element. I will find a solution to this soon. – shreyasm-dev Nov 06 '20 at 16:17
0

Ok, I think I've finally done it.

function doFindStringAndMoveCaret(editor, findTx) {
        treeObject = {}
        var element = editor;
        editor.focus();

        function doTreeWalk(element, object) {
            var nodeList = element.childNodes;
            if (nodeList != null) {
                if (nodeList.length) {
                    object[element.nodeName] = []; 

                    for (var i = 0; i < nodeList.length; i++) {

                        if (nodeList[i].nodeType == 3) {
                            var p=nodeList[i].nodeValue.indexOf(findTx);
                            if ( p!=-1 ) {
                                var sel=window.getSelection();
                                var range=sel.getRangeAt(0);
                                range.setStart(nodeList[i], p);
                                range.setEnd(nodeList[i], p+findTx.length);
                                return;
                            }
                        }
                        else {
                            object[element.nodeName].push({});
                            doTreeWalk(nodeList[i]
                            , object[element.nodeName][object[element.nodeName].length - 1]);
                        }
                    }
                }
            }
        }

        doTreeWalk(element, treeObject);
        return;
    }
    doFindStringAndMoveCaret(document.getElementById('mydiv'), 'peekabo');

A working example may be found on https://jsfiddle.net/Abeeee/krLeop49/6/

Thanks anyways

Regards Abe

user1432181
  • 918
  • 1
  • 9
  • 24
-2
document.getElementById(elem).focus

Try this Or this

textSelect(document.getElementById(elem), 1);
804k
  • 3
  • 3