Brian McCutchon
Brian McCutchon

Reputation: 8584

Set Selection Start and End in a Contentedible

I am attempting to create a JavaScript editor for a web page that employs syntax-based formatting. It completely replaces the innerHTML of a <pre> tag using onkeyup. However, this results in the caret being moved to the beginning of the editor.

The only solution that I can find is to get the offset of the selection start and end before changes are made, then set these at the end. My code for getting the selection start and end is modified from https://stackoverflow.com/a/4812022/2093695:

var selStart, selEnd;
if (typeof window.getSelection != "undefined") {
    var range = window.getSelection().getRangeAt(0);
    var preCaretRange = range.cloneRange();
    preCaretRange.selectNodeContents(editor);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    selStart = preCaretRange.toString().length;
    selEnd = selStart + range.toString().length;
} else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
    var textRange = document.selection.createRange();
    var preCaretTextRange = document.body.createTextRange();
    preCaretTextRange.moveToElementText(editor);
    preCaretTextRange.setEndPoint("EndToEnd", textRange);
    selStart = preCaretTextRange.text.length;
    selEnd = selStart + textRange.text.length;
} else selStart = 0;

That part works fine, in Firefox, at least. My problem is with setting the selection. What I have so far is as follows:

// restore selection
if (selStart) {
    if (range) {
        range = window.getSelection().getRangeAt(0);
        range.setStart(editor,selStart);
        range.setEnd(editor,selEnd);
        // Doesn't work, in FF at least
        // Behavior: when there are no tags,
        //     for 0, caret set at beginning
        //     for 1, caret set at end
        //     for anything greater, "IndexSizeError: Index or size is negative or greater than the allowed amount"
        // when the editor contains tags,
        //     for 0, caret set at beginning of editor
        //     for 1, caret set at beginning of first tag (unless same as start of editor)
        //     for 2, caret set at end of first tag
        //     for 3, caret set at beginning of second tag
        //     for 4, caret set at end of second tag, and so on
        //     for a number that is 1 greater than whatever number would set the caret at the end of the last tag, caret set at end of editor
        //     for a number greater than that, same error as before
    } else if (document.selection && document.selection.createRange) {
        // Not sure what to do here
    }
}

Is that how setStart and setEnd are supposed to work? What am I doing wrong? Is there a better way to set the selection start and end, preferably without using a library?

Upvotes: 4

Views: 10189

Answers (1)

Tim Down
Tim Down

Reputation: 324567

I've posted a couple of different functions for this. I can't find my most recent one but the following has a link to jsFiddle containing a restoreSelection() function that should be helpful:

https://stackoverflow.com/a/7404126/96100

Update: I've found my more recent version of this code. It should work the same but the internals are a bit more elegant.

https://stackoverflow.com/a/13950376/96100

Upvotes: 6

Related Questions