Skip to content Skip to sidebar Skip to footer

Error When Trying To Use Setstart And Setend On Range: Uncaught Indexsizeerror: Failed To Execute 'setend' On 'range'

When I try to use a range, I get this error in the console: Uncaught IndexSizeError: Failed to execute 'setEnd' on 'Range': The offset 2 is larger than or equal to the node's lengt

Solution 1:

I found a solution to the error with one line:

functiongotMark() {
    var range = document.createRange();
    var startNode = document.getElementById("dividTeste1").firstChild;

    range.setStart(startNode, 0);
    range.setEnd(startNode, 2);

    var newNode = document.createElement("span");
    newNode.className = 'highlight-yellow'; // <-- added

    range.surroundContents(newNode);
}

Now the selection has been spanned.

Solution 2:

While OP's code seems to be safe and in bounds, the problem often arises when making multiple manipulations on the same node, which changes its characteristics and throws off subsequent offset computations you may have made.

For example, let's say we want to add highlighting to a paragraph on an array of start-end pairs. The naive approach is to iterate forward over the pairs and perform the insertions for each start-end range:

consthighlightPositions = (textEl, positions) =>
  positions.forEach(([start, end]) => {
    const range = document.createRange();
    range.setStart(textEl.firstChild, start);
    range.setEnd(textEl.firstChild, end);
    const span = document.createElement("span");
    span.style.background = "#0f6";
    range.surroundContents(span);
  })
;

const positions = [[0, 3], [8, 11]];
highlightPositions(document.querySelector("p"), positions);
<p>foo bar baz</p>

The fix here is to ensure the positions are sorted and non-overlapping, then iterate backwards so that when the node's content changes, the index offsets earlier in the contents won't be affected.

consthighlightPositions = (textEl, positions) =>
  positions.slice().reverse().forEach(([start, end]) => {
    const range = document.createRange();
    range.setStart(textEl.firstChild, start);
    range.setEnd(textEl.firstChild, end);
    const span = document.createElement("span");
    span.style.background = "#0f6";
    range.surroundContents(span);
  })
;

const positions = [[0, 3], [8, 11]];
highlightPositions(document.querySelector("p"), positions);
<p>foo bar baz</p>

The problem can also arise when computing offsets that span across multiple nodes; make sure the offset is local to node given as the range's first argument.

There are many other strategies for avoiding the problem, including Node.normalize() to glue text children back together, building up the desired text on a new node or string and setting .innerHTML when it's ready, etc.

Solution 3:

consthighlightPositions = (textEl, positions) =>
  positions.slice().reverse().forEach(([start, end]) => {
    const range = document.createRange();
    range.setStart(textEl.firstChild, start);
    range.setEnd(textEl.firstChild, end);
    const span = document.createElement("span");
    span.style.background = "#0f6";
    range.surroundContents(span);
  })
;

const positions = [[0, 3], [8, 11]];
highlightPositions(document.querySelector("p"), positions);
<p>foo bar baz</p>

Post a Comment for "Error When Trying To Use Setstart And Setend On Range: Uncaught Indexsizeerror: Failed To Execute 'setend' On 'range'"