未捕获的IndexSizeError:无法在'Selection'上执行'getRangeAt':0不是有效的索引

Yus*_*eev 22 javascript

这有什么问题?

if ( window.getSelection() ) 
  EditField = window.getSelection().getRangeAt(0);
Run Code Online (Sandbox Code Playgroud)

抛出错误:

未捕获的IndexSizeError:无法在"选择"上执行"getRangeAt":0不是有效索引.

Ruu*_*man 29

问题似乎是WebKit特有的; 我无法在IE或Firefox中重现它.正如OP已经提到的,Google Chrome上的错误是:

未捕获的IndexSizeError:无法在'Selection'上执行'getRangeAt':0不是有效的索引

Safari有同样的问题,但消息不同:

INDEX_SIZE_ERR:DOM例外1:索引或大小为负数,或大于允许值.

通常发生在简单的WYSIWYG编辑器中.例:

<form name="frm">
    <div contenteditable="true" style="border:1px solid grey;height:8em;width:100%;">
    Text inside my editor.
    </div>
    Click this button to insert some text into the text editor:
    <button type="button" onclick="doInsert('TEST');">Insert</button>
</form>

<script>
    function doInsert(text) {
        var sel = window.getSelection && window.getSelection();
        if (sel) {
            var range = sel.getRangeAt(0);              // error here
            var node = document.createTextNode(text);
            range.deleteContents();
            range.insertNode(node);
        }
    }
</script>
Run Code Online (Sandbox Code Playgroud)

页面加载后,单击按钮; 您将在JavaScript控制台(Chrome)或错误控制台(Safari)中看到错误消息.但是,如果单击按钮之前单击编辑器部分内的任何位置,则不会发生错误.

这个问题很容易预防; 总是rangeCount在打电话前检查getRangeAt:

function doInsert(text) {
    var sel = window.getSelection && window.getSelection();
    if (sel && sel.rangeCount > 0) {
        var range = sel.getRangeAt(0);
        ...
Run Code Online (Sandbox Code Playgroud)

当然,您可以通过focus在页面加载时给予编辑器焦点(使用方法)来避免这种情况.

但是,有一些罕见的事件会导致编辑器稍后失去其选择范围.到目前为止,我只能通过多选列表控件重现这一点:

<form name="frm">
  <div contenteditable="true" style="border:1px solid grey;height:8em;width:100%;">
    Click to position the caret anywhere inside this editor section.
  </div>
  Next, select an item from this list:
  <select id="insertables" multiple="multiple">
    <option>foo</option>
    <option>bar</option>
    <option>baz</option>
  </select>
  <br />Finally, click this button to insert the list item into the text editor:
  <button type="button" onclick="doInsert(frm.insertables.value);">Insert</button>
</form>

<script>
  function doInsert(text) {
    var sel = window.getSelection && window.getSelection();
    if (sel) {
      var range = sel.getRangeAt(0); // error here
      var node = document.createTextNode(text);
      range.deleteContents();
      range.insertNode(node);
    }
  }
</script>
Run Code Online (Sandbox Code Playgroud)

同样,我可以通过检查来防止错误rangeCount.但问题仍然是我的按钮没有插入最后一个已知插入位置的所选列表项,正如它应该做的那样.方法focus没有帮助; 它会将插入符号位置重置为编辑器中的左上角位置.

解决方案是持续保存当前选择范围(为此目的,陷阱事件SelectionChange):

var savedRange = null;

document.addEventListener("selectionchange", HandleSelectionChange, false);

function HandleSelectionChange() {
    var sel = window.getSelection && window.getSelection();
    if (sel && sel.rangeCount > 0) {
        savedRange = sel.getRangeAt(0);
    }
}
Run Code Online (Sandbox Code Playgroud)

单击按钮时恢复它:

function doInsert(text) {
    var sel = window.getSelection && window.getSelection();
    if (sel && sel.rangeCount == 0 && savedRange != null) {
        sel.addRange(savedRange);
    }
    if (sel && sel.rangeCount > 0) {
        var range = sel.getRangeAt(0);
        var node = document.createTextNode(text);
        range.deleteContents();
        range.insertNode(node);
    }
}
Run Code Online (Sandbox Code Playgroud)

小提琴:http://jsfiddle.net/stXDu/