xmxmxmx
xmxmxmx

Reputation: 439

window.getSelection() how to get the current node inside contenteditable javascript

I have this function which moves caret correctly inside the first line of a contenteditable although it only looks at childNodes[0] so it doesn't move caret inside the second line.

function setcaret(item, pos) {
  var range = document.createRange();
  var sel = window.getSelection();

  range.setStart(item.childNodes[0], pos);
  range.collapse(false);
  sel.removeAllRanges();
  sel.addRange(range);
  item.focus();
}

setcaret(divid, 4); //move caret four places in current line

I need to modify this to get the current node so it will work for multiple divs inside the contenteditable like if the caret is inside the second node

<div contenteditable="true"><div>first child</div><div>second |child</div>

Any ideas?

Upvotes: 1

Views: 1048

Answers (1)

Kaiido
Kaiido

Reputation: 136707

That is unfortunately not that easy...

To do this, we have to determine ourself which node the character at given index belongs to. And to do this, we need to walk over the childNodes of our container.

This operation can be greatly simplified with a TreeWalker, set up to only iterate over TextNodes:

// this method will allow us to get the TextNode at character index
function getNodeAtCharPos(parent, pos) {
  var walker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);
  var chars = 0;
  var nodeLength = 0;
  while (chars < pos && walker.nextNode()) {
    nodeLength = walker.currentNode.textContent.length;
    chars += nodeLength;
  }
  return {
    node: walker.currentNode,
    index: Math.min(pos - (chars - nodeLength), nodeLength)
  };
}

function setcaret(item, pos) {
  if (pos < 0) return;
  var nodeAtPos = getNodeAtCharPos(item, pos);
  var range = document.createRange();
  var sel = window.getSelection();
  range.setStart(nodeAtPos.node, nodeAtPos.index);
  sel.removeAllRanges();
  sel.addRange(range);
}
var divid = document.getElementById('divid');
btn.onclick = e => setcaret(divid, inp.value);
<div id="divid" contenteditable="true"><div>first child</div><div>second child</div></div>

<br><label>Offset: <input id="inp" type="number" value="4" min="0"><button id="btn">set caret</button></label>

Upvotes: 3

Related Questions