Skip to content
Advertisement

Find range of selected text wrt parent node

I want to find the range of the selected text with respect to the parent element. So in this <p> element, the anchorOffset & focusOffset for “fox” is [16,19]

<p>The quick brown fox jumps over the lazy dog.</p>

But if we add a <b> tag before “fox” then the values change to [1,4].

<p>The quick <b>brown</b> fox jumps over the lazy dog.</p>

My best guess is, the index count starts from the end of </b> tag. But I want it to still show the original value, irrespective of the HTML within <p>. For this, I tried creating a Range, but still couldn’t figure it out. Following is the code, as a function.

function rangefun() {
  var range = document.createRange();
  var referenceNode = document.getElementsByTagName('P').item(0);
  range.selectNode(referenceNode);
  var selection = window.getSelection();
  var start = selection.anchorOffset;
  var end = selection.focusOffset;
  console.log("start: " + start);
}

Advertisement

Answer

Below is a modified version of your code that does what you want.

It takes both the anchor and extent nodes which are children of <p> from the selection variable (i.e. window.getSelection()).

These two nodes are passed to the calculateOffset function. For each of the nodes, the sum of the text length of the preceding siblings is calculated. Incrementing this total text length with the selection’s relative offset (the one in the child node) will yield the start and end offsets with respect to the <p> text length.

function rangefun() {
  var selection = window.getSelection();
  var start = selection.anchorOffset;
  var end = selection.extentOffset;
  var anchorNode = selection.anchorNode;
  var extentNode = selection.extentNode;

  var startIndex = calculateOffset(anchorNode, start);
  var endIndex = calculateOffset(extentNode, end);
  console.log('start: ' + startIndex);
  console.log('end: ' + endIndex);
}

function calculateOffset(child, relativeOffset) {
  var parent = child.parentElement;
  
  // verify whether or not the correct parent element is selected, modify if necessary
  if (parent.tagName != 'P') {
    parent = parent.closest('p');
    child = child.parentElement;
  }
  var children = [];

  // add the child's preceding siblings to an array
  for (var c of parent.childNodes) {
    if (c === child) break;
    children.push(c);
  }

  // calculate the total text length of all the preceding siblings and increment with the relative offset
  return relativeOffset + children.reduce((a, c) => a + c.textContent.length, 0);
}
Advertisement