6

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

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

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

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

但是,当您第一次点击,然后通过点击然后尝试将storeHTML 替换为完全相同的 HTML时,什么也没有发生。overwrite htmlrestore

我认为使用.cloneRange()会有所作为,但事实并非如此。即使是对象 ( ) 的深层副本$.extend(true, {}, oldRange)也无法解决问题。一旦我覆盖 HTML,选择对象sel也会被更改。更改选择上下文将擦除范围对我来说很有意义,但我正在尝试将其恢复为完全相同的 HTML。

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

注意:只有 Firefox/Chrome,所以不需要跨浏览器黑客。

更新:

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

现在,当我尝试恢复 iframe 的主体时,出现以下错误:TypeError: Value does not implement interface Node.在以下行preSelectionRange.selectNodeContents(containerEl);中。我没有从谷歌搜索中得到太多。我试图包装正文的内容并恢复包装的 html,但我得到了同样的错误。

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

没有包装也一样:snipt.org/AJaf0

更新 2: 我认为我必须使用editable.get(0),当然。但现在 iframe 选择的startandend为 0。请参阅snipt.org/AJah2

4

2 回答 2

16

您可以使用以下函数保存和恢复字符位置:

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();
    };
}
于 2013-07-17T08:26:06.043 回答
1

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

替换那条线

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

经过

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

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

于 2021-07-25T14:41:02.867 回答