最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Error when trying to use setStart and setEnd on Range: Uncaught IndexSizeError: Failed to execute 'setEnd&a

programmeradmin2浏览0评论

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 length (0).

Here is my script:

<script type="text/javascript">
    function gotMark() {
        var range = document.createRange();
        var startNode = document.getElementById("dividTeste1").firstChild;

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

        var newNode = document.createElement("span");
        range.surroundContents(newNode);
    }
</script>

And page:

<div class="ui-block-a" id="divid" value="div1">
    <div style="background-color: black; color: white; padding: 20px;" id="divid2">
        <h2>London</h2>
        <p id="dividTeste1">London is the capital city of England. It is the most
            populous city in the United Kingdom, with a metropolitan area of
            over 13 million inhabitants.</p>
    </div>
        
    <div style="background-color: black; color: white; padding: 20px;" id="divid2">
        <h2>London</h2>
        <p id="dividTeste2">London is the capital city of England. It is the most
            populous city in the United Kingdom, with a metropolitan area of
            over 13 million inhabitants.</p>
    </div>
</div>
<input type="button" value="Marcar  range" onClick="gotMark()"
        id="marcarconranger12" />

I tried following the instructions from dom range.setStart / setEnd to select the start and end position.

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 length (0).

Here is my script:

<script type="text/javascript">
    function gotMark() {
        var range = document.createRange();
        var startNode = document.getElementById("dividTeste1").firstChild;

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

        var newNode = document.createElement("span");
        range.surroundContents(newNode);
    }
</script>

And page:

<div class="ui-block-a" id="divid" value="div1">
    <div style="background-color: black; color: white; padding: 20px;" id="divid2">
        <h2>London</h2>
        <p id="dividTeste1">London is the capital city of England. It is the most
            populous city in the United Kingdom, with a metropolitan area of
            over 13 million inhabitants.</p>
    </div>
        
    <div style="background-color: black; color: white; padding: 20px;" id="divid2">
        <h2>London</h2>
        <p id="dividTeste2">London is the capital city of England. It is the most
            populous city in the United Kingdom, with a metropolitan area of
            over 13 million inhabitants.</p>
    </div>
</div>
<input type="button" value="Marcar  range" onClick="gotMark()"
        id="marcarconranger12" />

I tried following the instructions from dom range.setStart / setEnd to select the start and end position.

Share Improve this question edited Jul 6, 2021 at 0:09 ggorlen 58k8 gold badges114 silver badges157 bronze badges asked Apr 23, 2015 at 18:08 Weslley BarbosaWeslley Barbosa 2353 silver badges12 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 1

I found a solution to the error with one line:

function gotMark() {
    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.

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 putations 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:

const highlightPositions = (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.

const highlightPositions = (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 puting 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.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论