HTML修改后无法恢复选择,即使它是相同的HTML

时间:2013-07-16 14:09:52

标签: javascript jquery range selection contenteditable

我正在尝试存储一个contentEditable元素的选择并稍后恢复。

我想观察paste事件并按原样保存HTML,清除html然后手动插入粘贴的文本,并在所选位置进行一些更改。

看一下这个例子:jsfiddle.net/gEhjZ

当您选择文本的一部分时,点击store,再次删除选择并点击restore,它会按预期运行。

但是,当您第一次点击store时,请点按overwrite html,然后点击restore然后尝试.cloneRange(),将HTML替换为完全相同的HTML。

我认为使用$.extend(true, {}, oldRange)会有所作为,但事实并非如此。即使是对象的深层副本(sel)也无法解决问题。只要我覆盖HTML,选择对象TypeError: Value does not implement interface Node.也会被更改。我觉得更改选择上下文会擦除范围,但我正在尝试将其恢复为完全相同的HTML。

我知道我可以使用rangy,但我真的不想仅仅为这个小功能使用一个巨大的库。我错过了什么?任何帮助将不胜感激!

注意:仅支持Firefox / Chrome,因此不需要使用crossbrowser-hack。

更新

@Tim Down的答案在使用div时有效,但我实际上是在使用iframe。当我做出这个例子时,我认为它没有任何区别。

现在,当我尝试恢复iframe的正文时,我收到以下错误:preSelectionRange.selectNodeContents(containerEl);位于以下行editable.get(0)中。我没有从谷歌搜索获得太多。我试图包装正文的内容并恢复包装的html,但我得到了同样的错误。

jsfiddle在这种情况下不起作用,因为它使用iframe来显示结果本身,所以我在这里举了一个例子:snipt.org/AJad3

同样没有包裹:snipt.org/AJaf0

更新2: 当然,我认为我必须使用start。但现在iframe选择的end和{{1}}为0.请参阅snipt.org/AJah2

2 个答案:

答案 0 :(得分:14)

您可以使用以下功能保存和恢复角色位置:

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

我稍微调整了这些功能,以便为iframe中的元素工作。

演示:http://jsfiddle.net/timdown/gEhjZ/4/

代码:

var saveSelection, restoreSelection;

if (window.getSelection && document.createRange) {
    saveSelection = function(containerEl) {
        var doc = containerEl.ownerDocument, win = doc.defaultView;
        var range = win.getSelection().getRangeAt(0);
        var preSelectionRange = range.cloneRange();
        preSelectionRange.selectNodeContents(containerEl);
        preSelectionRange.setEnd(range.startContainer, range.startOffset);
        var start = preSelectionRange.toString().length;

        return {
            start: start,
            end: start + range.toString().length
        };
    };

    restoreSelection = function(containerEl, savedSel) {
        var doc = containerEl.ownerDocument, win = doc.defaultView;
        var charIndex = 0, range = doc.createRange();
        range.setStart(containerEl, 0);
        range.collapse(true);
        var nodeStack = [containerEl], node, foundStart = false, stop = false;

        while (!stop && (node = nodeStack.pop())) {
            if (node.nodeType == 3) {
                var nextCharIndex = charIndex + node.length;
                if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
                    range.setStart(node, savedSel.start - charIndex);
                    foundStart = true;
                }
                if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
                    range.setEnd(node, savedSel.end - charIndex);
                    stop = true;
                }
                charIndex = nextCharIndex;
            } else {
                var i = node.childNodes.length;
                while (i--) {
                    nodeStack.push(node.childNodes[i]);
                }
            }
        }

        var sel = win.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    };
} else if (document.selection) {
    saveSelection = function(containerEl) {
        var doc = containerEl.ownerDocument, win = doc.defaultView || doc.parentWindow;
        var selectedTextRange = doc.selection.createRange();
        var preSelectionTextRange = doc.body.createTextRange();
        preSelectionTextRange.moveToElementText(containerEl);
        preSelectionTextRange.setEndPoint("EndToStart", selectedTextRange);
        var start = preSelectionTextRange.text.length;

        return {
            start: start,
            end: start + selectedTextRange.text.length
        };
    };

    restoreSelection = function(containerEl, savedSel) {
        var doc = containerEl.ownerDocument, win = doc.defaultView || doc.parentWindow;
        var textRange = doc.body.createTextRange();
        textRange.moveToElementText(containerEl);
        textRange.collapse(true);
        textRange.moveEnd("character", savedSel.end);
        textRange.moveStart("character", savedSel.start);
        textRange.select();
    };
}

答案 1 :(得分:1)

提供的解决方案效果很好。

替换那条线

if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {

if (!foundStart && savedSel.start >= charIndex && savedSel.start < nextCharIndex) {

防止 Chrome/Edge 选择上一行的结尾