谷歌浏览器和Chromium中不支持非连续选择错误

10

我正在开发一个书签应用程序,需要存储用户选择的关键词、单词或内容。 我正在使用createRange()和addRange() JavaScript方法创建范围,然后找出用户选择的元素/内容。 我编写的代码如下。

<head>
<script type="text/javascript">
    var storedSelections = [];

    function StoreSelection () {
        if (window.getSelection) {
            var currSelection = window.getSelection ();
            for (var i = 0; i < currSelection.rangeCount; i++) {
                storedSelections.push (currSelection.getRangeAt (i));
            }
            currSelection.removeAllRanges ();
        } else {
            alert ("Your browser does not support this example!");
        }
    }

    function ClearStoredSelections () {
        storedSelections.splice (0, storedSelections.length);
    }

    function ShowStoredSelections () {
        if (window.getSelection) {
            var currSelection = window.getSelection ();
            currSelection.removeAllRanges ();
            for (var i = 0; i < storedSelections.length; i++) {
                currSelection.addRange (storedSelections[i]);
            }
        } else {
            alert ("Your browser does not support this example!");
        }
    }
</script>
</head>
<body>
    Select some content on this page and use the buttons below.<br />         <br />
    <button onclick="StoreSelection ();">Store the selection</button>
    <button onclick="ClearStoredSelections ();">Clear stored selections
</button>
    <button onclick="ShowStoredSelections ();">Show stored selections</button>

</body>

在Firefox上,这段代码运行得很完美。我可以一个接一个地选择多个内容,并能够再次显示所选的内容,但是在Chrome和Chromium上,当我将多个元素存储在范围数组中并单击“显示已存储的选择”按钮时,会出现“不连续选择不受支持”的错误提示。

如果有其他替代方法来完成这个书签任务,请建议并感谢您的帮助。


这个链接能帮到你吗? - Siguza
虽然只是一个警告,但如果在 Firefox 和 Chrome 中运行代码,你会看到功能上的差异,并且会发现这是 Chromium 和 Chrome 中的错误。 - Sajid Ahmad
刚在OS X 10.10上使用Chrome(40.0.2214.93,64位)和Safari(8.0.2,构建10600.2.5)进行了测试 - 两者都可以正常工作,并且控制台中没有任何内容。 - Siguza
我在Ubuntu 14.04上又试了一次,使用Chrome 40.0.2214.93:完美运行。你用的是哪个版本的Chrome? - Siguza
选择一个单词,然后点击“存储所选项”按钮,然后再选择另一个单词,再次点击“存储所选项”按钮,最后点击“显示已存储的选项”按钮。可能会出现错误。 - Sajid Ahmad
显示剩余7条评论
3个回答

9

6

这是我能想到的唯一可能的方法:

<span style="background: Highlight;">...</span>包装所选内容。

但请注意:

  • 显然,你必须在选择其他内容时立即删除这些标签,但这不应该太难。但是,你应该使用window.onmousedown而不是window.onclick来实现,因为onclick会在按下任何按钮后触发,因此当按下“显示存储的选择”按钮时,将创建一个新的选择,从而破坏原本应该捕获的选择。
  • 移除或替换存储选择开始或结束的任何元素都将使该选择无效,因此当单击“显示存储的选择”时,将不会显示任何内容。
  • 如果选择跨越多个元素,则需要将其拆分为每个元素的一个选择,否则插入标签将失败或将其他元素(如按钮)切成两半。

以下代码(fiddle)是我能做到的最好的:

var storedSelections = [];
var simulatedSelections = [];

window.onmousedown = clearSimulatedSelections;

function storeSelection()
{
    if(window.getSelection)
    {
        var currSelection = window.getSelection();
        for(var i = 0; i < currSelection.rangeCount; i++)
        {
            storeRecursive(currSelection.getRangeAt(i));
        }
        currSelection.removeAllRanges();
    }
    else
    {
        alert("Your browser does not support this example!");
    }
}

function storeRecursive(selection, node, started)
{
    node = node || document.body;
    started = started || false;
    var nodes = node.childNodes;
    for(var i = 0; i < nodes.length; i++)
    {
        if(nodes[i].nodeType == 3)
        {
            var first = nodes[i] == selection.startContainer;
            var last = nodes[i] == selection.endContainer;
            if(first)
            {
                started = true;
            }
            if(started)
            {
                var sel = selection.cloneRange();
                if(!first)
                {
                    sel.setStartBefore(nodes[i]);
                }
                if(!last)
                {
                    sel.setEndAfter(nodes[i]);
                }
                storedSelections.push(sel);
                if(last)
                {
                    return false;
                }
            }
        }
        else
        {
            started = storeRecursive(selection, nodes[i], started);
        }
    }
    return started;
}

function clearStoredSelections()
{
    storedSelections = [];
}

function showStoredSelections()
{
    if(window.getSelection)
    {
        var currSelection = window.getSelection();
        currSelection.removeAllRanges();
        for(var i = 0; i < storedSelections.length; i++)
        {
            var node = document.createElement("span");
            node.className = "highlight";
            storedSelections[i].surroundContents(node);
            simulatedSelections.push(node);
        }
    }
    else
    {
        alert("Your browser does not support this example!");
    }
}

function clearSimulatedSelections()
{
    for(var i = 0; i < simulatedSelections.length; i++)
    {
        var sec = simulatedSelections[i];
        var pn = sec.parentNode;
        while(sec.firstChild)
        {
            pn.insertBefore(sec.firstChild, sec);
        }
        pn.removeChild(sec);
    }
    simulatedSelections = [];
}
.highlight
{
    background: Highlight;
}
Select some content on this page and use the buttons below.<br><br>
<button onclick="storeSelection();">Store the selection</button>
<button onclick="clearStoredSelections();">Clear stored selections</button>
<button onclick="showStoredSelections();">Show stored selections</button>

它在Firefox、Safari和Chrome中运行,但有以下缺点:
  • 跨多行的选择不会选择行末和父元素边框之间的空白区域,就像实际选择一样。
  • 有时,在存储的选择开始点之前的位置开始选择时,显示它们会合并范围,因此还会选择之间的文本。对存储选择的数组进行排序似乎没有帮助。
  • 在Safari中,当选择多行并在按钮文本的中间结束/开始选择时,选项卡会多次崩溃,并出现分段错误。

然而,我怀疑除了Firefox以外的浏览器可能没有更好的解决方案,但即使Firefox也有一个删除不连续选择的票


当你在 Chrome 中切换设备工具栏并刷新页面时,会出现这个错误。 - Adam Clark
在设备工具栏的devtools中刷新后,会出现该错误。https://www.dropbox.com/s/ngab9c9ydy1r9ye/Screenshot%202016-08-23%2015.10.25.png?dl=0 - Adam Clark
@AdamClark 我相当确定那不是由我的代码引起的。能否提供完整的代码链接? - Siguza
不,当然不是。我今天下午遇到了这个错误并开始调查... 这是我看到的第一个stackoverflow问题,感觉任何寻找类似原因的人都会欣赏我的见解。 - Adam Clark

3

提醒一下,当我自己实现“复制到剪贴板”功能时,我遇到了类似的错误。我不会讨论原帖中提供的代码,但我会告诉你我如何在自己的代码中解决这个问题。

复现:

  1. 复制页面上的其他文本到剪贴板,例如“foo”。
  2. 粘贴文本到某个地方。它输出“foo”。
  3. 点击“复制到剪贴板”按钮,将复制“bar”。
  4. 将文本粘贴到某个地方。

预期结果:

输出“bar”。

实际结果:

输出“不支持不连续选择”。

修复方法:

在“复制到剪贴板”事件处理程序开始处调用window.getSelection().removeAllRanges()。“Discontiguous”表示“不连接”。因此,我的猜测是浏览器复制第一个范围(包含“foo”的节点),然后在尝试选择不与第一个节点相邻的另一个范围时变得愤怒。


完美,这对我很有效,而且这个答案比其他答案简单多了。 - Rav
可能已经修复了这个漏洞,截至2月20日 - https://bugs.chromium.org/p/chromium/issues/detail?id=353069#c4 - Dropout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接