最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Set Selection Start and End in a Contentedible - Stack Overflow

programmeradmin0浏览0评论

I am attempting to create a JavaScript editor for a web page that employs syntax-based formatting. It pletely 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 :

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?

I am attempting to create a JavaScript editor for a web page that employs syntax-based formatting. It pletely 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./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?

Share Improve this question edited May 23, 2017 at 10:33 CommunityBot 11 silver badge asked Apr 5, 2013 at 2:25 Brian McCutchonBrian McCutchon 8,6044 gold badges35 silver badges45 bronze badges 2
  • 1 Keep in mind directly editing innerHTML causes the undo/redo buffer to be erased. Ran into similar problems developing tabIndent.js. – Julian H. Lam Commented Apr 5, 2013 at 2:58
  • @Julian: Thanks for the heads-up. I am aware of this, and considering using some kind of alternative, such as making the entire contents of the the editor a range and using something like var code = range.toString(); followed by range.deleteContents();, code = range.createContextualFragment(code);, and range.insertNode(code); – Brian McCutchon Commented Apr 5, 2013 at 3:08
Add a ment  | 

1 Answer 1

Reset to default 6

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./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./a/13950376/96100

发布评论

评论列表(0)

  1. 暂无评论