Yusup Bugacheev
Yusup Bugacheev

Reputation: 511

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

What is problem here?

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

throws error:

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

Upvotes: 34

Views: 46693

Answers (5)

narcissus313
narcissus313

Reputation: 109

i had the same problem for any button i used in my development. i deactivated a chrome extension named "Select to translate - Translator, Dictionary" and the problem solved! deactivate the chrome extensions one by one to find the extension that makes the problem.

Upvotes: 0

Ruud Helderman
Ruud Helderman

Reputation: 11028

The problem seems to be WebKit-specific; I could not reproduce it in IE or Firefox. As OP already mentioned, the error on Google Chrome is:

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

Safari has the same issue, but the message is different:

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

Typically happens in simple WYSIWYG editors. Example:

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

Once the page has loaded, click the button; you will see the error message in the JavaScript console (Chrome) or error console (Safari). But if you click anywhere inside the editor section before clicking the button, the error will not occur.

This problem is easy to prevent; always check rangeCount before calling getRangeAt:

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

Of course, you can avoid the situation by giving the editor focus (with method focus) upon page load.

However, there are rare events that cause your editor to lose its selection range later on. So far, I have only been able to reproduce this with a multi-select list control:

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

Again, I can prevent the error by checking rangeCount. But the problem remains that my button does not insert the selected list item at the last known caret position, as it was supposed to do. Method focus does not help; it will reset the caret position to the top left position in the editor.

A solution is to continuously save the current selection range (for this purpose, trap event 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);
    }
}

And restore it when the button is clicked:

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

Fiddle: http://jsfiddle.net/stXDu/

Upvotes: 45

John
John

Reputation: 13756

The exact way to address this problem (like 90% of everything in JavaScript) is object detection; selection related isn't as straight forward of course:

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

To test throw window.getSelection().rangeCount in to a console when there is no selected text and it'll return 0; with a selection it'll return 1. I am not sure however if and how you could manage to get it to return 2 or greater.

Upvotes: 4

shuizhongyuemin
shuizhongyuemin

Reputation: 579

I have some solution in my project

use iframe as WYSIWYG editor

we set the editwin as the window of the iframe

every time before insert something to the editor by code, you should use the below code first.

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

use textarea as editor

set textobj as the dom of the textarea

same way like up, but different code

textobj.focus();

Upvotes: 2

MarzSocks
MarzSocks

Reputation: 4318

Here is something to consider: have you disabled selection somewhere in your code? If so, in some situations you may want to first enable selection, preferably in a mousedown event or before focusing on your element (NB!), before getting the range. Doing this has solved my version of the problem.

$('#MyEditableDiv').on('mousedown', function (e) {

    $(document).enableSelection();


});

Upvotes: 0

Related Questions