如何以编程方式仅选择用户可以选择的所有文本节点

时间:2019-01-12 15:42:35

标签: javascript selection user-select

我需要使用DOM选择API getSelection选择HTML文档中的所有节点。
用户不能选择的节点(即用鼠标)不能使用。
因此,如果一个元素应用了CSS规则user-select: none-moz-user-select: none,则我的编程选择应排除这些元素。

如果我手动(通过鼠标)选择文本,则不会选择这些元素。如果我将window.getSelection().selectAllChildren应用于其父元素之一,则非可选元素也会被选中。

我尝试了SelectionRange对象的不同方法,但是还没有找到只以编程方式选择那些可以手动选择的元素的方法。

<body>
    <div>Selectable</div>
    <div style="-moz-user-select:none">
        <span id="span">Non-Selectable</span>
    </div>

    <script>
        const sel = window.getSelection();
        sel.selectAllChildren(document.body);
        console.log(sel.containsNode(document.getElementById('span')));
        // outputs true
    </script>
</body>  

有人知道一种以编程方式仅选择那些可以手动选择的元素的方法吗?

编辑因此,我需要一个函数,该函数接收节点作为参数并在该节点可选的情况下返回布尔值:

function isSelectable(node) {
    // determine if node is selectable
}

3 个答案:

答案 0 :(得分:1)

可能是这样的:

-

基本上,如果此元素或其任何祖先是不可选择的,那么此元素也是不可选择的。我们先检查此元素,然后使用递归检查祖先元素,在我们用完祖先或发现设置为不可选择的元素时停止。

我对用户选择的工作方式的假设可能是错误的;即使将祖先设置为不可选择,也有可能迫使内部元素可选择。逻辑可以重新组织以减少混乱。当然可以使用循环来删除递归。 var userselect = [ '-webkit-touch-callout', /* iOS Safari */ '-webkit-user-select', /* Safari */ '-khtml-user-select', /* Konqueror HTML */ '-moz-user-select', /* Firefox */ '-ms-user-select', /* Internet Explorer/Edge */ 'user-select' ]; function isSelectable(element) { var style = getComputedStyle(element); var canSelect = !userselect.some(key => style[key] === 'none'); if(canSelect) { if(element.parentElement) return isSelectable(element.parentElement); return true; } return false; } 数组可以使用一些智能;如果这是扩展,则可以使用它来告知需要检查的属性。此代码需要元素而不是节点。我尚未真正测试过此代码,但似乎应该可以。

答案 1 :(得分:0)

好吧,我怀疑您的代码部分不错(99%不错),这是因为浏览器不同,结合了我已经发送给您的脚本和链接,我对此进行了管理:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <style>
    .noselect {
  -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome and Opera */
}
  </style>
</head>
<body>
    <div>Selectable</div>
    <div class="noselect">
        <span id="span">Non-Selectable</span>
    </div>
    <div id="r">
    </div>


    <script>
      window.onload = function() {
        var sel = window.getSelection();
        sel.selectAllChildren(document.body);
        document.getElementById('r').innerHTML = sel.containsNode(document.getElementById('span'));
        // outputs true
      };

    </script>
</body>
</html>

在此处运行时,您会看到它有效!我的意思是-moz-user-select:无;仅适用于Firefox ...

在说我也检查了其他浏览器(IE,Firefox,Chrome和Edge)之后,此方法仅在Chrome中有效。

答案 2 :(得分:0)

这是一种无需循环通过节点祖先的方法:

function isSelectable(textNode) {
    const selection = getSelection();
    selection.selectAllChildren(textNode.parentNode);
    const selectable = !!selection.toString();
    selection.collapseToStart();
    return selectable;
}

说明:
如果节点不是用户可选择的,您仍然可以通过编程方式选择它(selectAllChildren),但是toString()仍然不会包含该节点的文本内容。
就我而言,我需要遍历document.body的所有文本节点,不幸的是,出于我的目的,此解决方案仍然太慢