【发布时间】:2014-04-08 11:16:02
【问题描述】:
这里有什么问题?
if ( window.getSelection() )
EditField = window.getSelection().getRangeAt(0);
抛出错误:
未捕获的 IndexSizeError:无法在“选择”上执行“getRangeAt”:0 不是有效索引。
【问题讨论】:
标签: javascript
这里有什么问题?
if ( window.getSelection() )
EditField = window.getSelection().getRangeAt(0);
抛出错误:
未捕获的 IndexSizeError:无法在“选择”上执行“getRangeAt”:0 不是有效索引。
【问题讨论】:
标签: javascript
这个问题似乎是 WebKit 特有的;我无法在 IE 或 Firefox 中重现它。正如 OP 已经提到的,谷歌浏览器上的错误是:
未捕获的 IndexSizeError:无法在“选择”上执行“getRangeAt”:0 不是有效索引
Safari 也有同样的问题,但信息不同:
INDEX_SIZE_ERR:DOM 异常 1:索引或大小为负数,或大于允许值。
通常发生在简单的所见即所得编辑器中。示例:
<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>
页面加载完毕后,点击按钮;您将在 JavaScript 控制台 (Chrome) 或错误控制台 (Safari) 中看到错误消息。但是,如果您在单击按钮之前单击编辑器部分内的任意位置,则该错误将不会发生。
这个问题很容易预防;在致电 getRangeAt 之前始终检查 rangeCount:
function doInsert(text) {
var sel = window.getSelection && window.getSelection();
if (sel && sel.rangeCount > 0) {
var range = sel.getRangeAt(0);
...
当然,您可以通过在页面加载时赋予编辑器焦点(使用方法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>
同样,我可以通过检查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);
}
}
并在点击按钮时恢复:
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);
}
}
【讨论】:
准确解决这个问题的方法(就像 JavaScript 中 90% 的问题一样)是对象检测;选择相关当然不是那么简单:
if (window.getSelection().rangeCount >= 1) {var r = window.getSelection().getRangeAt(0);}
在没有选定文本时测试将window.getSelection().rangeCount 输入到控制台,它会返回0;选择它会返回 1。但是我不确定您是否以及如何设法让它返回 2 或更大。
【讨论】:
我的项目中有一些解决方案
使用 iframe 作为所见即所得的编辑器
我们将editwin设置为iframe的窗口
每次通过代码向编辑器插入内容之前,您应该先使用下面的代码。
editwin.focus();
editwin.document.body.focus();
使用 textarea 作为编辑器
设置textobj为textarea的dom
与 up 方法相同,但代码不同
textobj.focus();
【讨论】:
这里有一点需要考虑:您是否在代码中的某处禁用了选择?如果是这样,在某些情况下,您可能希望首先启用选择,最好是在 mousedown 事件中或在关注您的元素之前(注意!),然后再获取范围。这样做已经解决了我的问题。
$('#MyEditableDiv').on('mousedown', function (e) {
$(document).enableSelection();
});
【讨论】: