关于javascript:未捕获的IndexSizeError:无法在“选择”上执行“ getRangeAt”:0不是有效的索引

Uncaught IndexSizeError: Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index

这是什么问题?

1
2
if ( window.getSelection() )
  EditField = window.getSelection().getRangeAt(0);

引发错误:

Uncaught IndexSizeError: Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index.


这个问题似乎是WebKit特有的。我无法在IE或Firefox中复制它。如前所述,OP在Google Chrome上的错误是:

Uncaught IndexSizeError: Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index

Safari存在相同的问题,但消息有所不同:

INDEX_SIZE_ERR: DOM Exception 1: Index or size was negative, or greater than the allowed value.

通常发生在简单的所见即所得编辑器中。例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<form name="frm">
   
    Text inside my editor.
   
    Click this button to insert some text into the text editor:
    <button type="button" onclick="doInsert('TEST');">Insert</button>
</form>


    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);
        }
    }

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

这个问题很容易预防。始终在调用getRangeAt之前检查rangeCount

1
2
3
4
5
function doInsert(text) {
    var sel = window.getSelection && window.getSelection();
    if (sel && sel.rangeCount > 0) {
        var range = sel.getRangeAt(0);
        ...

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

但是,有些罕见的事件会导致您的编辑器稍后失去选择范围。到目前为止,我只能使用多选列表控件来重现此内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<form name="frm">
 
    Click to position the caret anywhere inside this editor section.
 
  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>


  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);
    }
  }

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

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

1
2
3
4
5
6
7
8
9
10
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);
    }
}

并在单击按钮时将其还原:

1
2
3
4
5
6
7
8
9
10
11
12
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);
    }
}

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


解决此问题的确切方法(例如JavaScript中90%的所有内容)是对象检测。选择相关当然不是那么简单:

1
if (window.getSelection().rangeCount >= 1) {var r = window.getSelection().getRangeAt(0);}

要在没有选定文本的情况下将window.getSelection().rangeCount抛出到控制台中,它将返回0;选择后它将返回1。但是我不确定是否以及如何设法使其返回2或更大。


我的项目中有一些解决方案

使用iframe作为WYSIWYG编辑器

我们将editwin设置为iframe的窗口

每次在通过代码向编辑器中插入内容之前,都应首先使用以下代码。

1
2
editwin.focus();
editwin.document.body.focus();

使用textarea作为编辑器

将textobj设置为textarea的dom

一样的方式,但是代码不同

1
textobj.focus();


这里有一些需要考虑的问题:您是否在代码的某处禁用了选择功能?如果是这样,则在某些情况下,您可能希望首先启用选择,最好在发生鼠标按下事件时或在专注于元素之前(NB!),然后再获取范围。这样做已经解决了我的问题版本。

1
2
3
4
5
6
$('#MyEditableDiv').on('mousedown', function (e) {

    $(document).enableSelection();


});