Как программно выделить только все текстовые узлы, которые могут быть выбраны пользователем - PullRequest
0 голосов
/ 12 января 2019

Мне нужно выбрать все узлы в документе HTML с помощью API выбора DOM getSelection.
Узлы, которые не могут быть выбраны пользователем (то есть с помощью мыши), должны быть исключены.
Итак, если к элементу применено правило CSS user-select: none или -moz-user-select: none, мой программный выбор должен исключить эти элементы.

Если я выделю текст вручную (с помощью мыши), эти элементы не будут выделены. Если я применяю window.getSelection().selectAllChildren к одному из его родительских элементов, то невыбираемый элемент также выделяется.

Я пробовал разные методы как для объектов Selection, так и Range, но не нашел способа выбрать программно только те элементы, которые можно выбрать вручную.

<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 голосов
/ 13 января 2019

Возможно что-то вроде этого:

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;
}

По сути, если этот элемент или любой из его предков недоступен для выбора, то этот элемент недоступен для выбора. Мы проверяем этот элемент, а затем используем рекурсию для проверки элементов-предков, останавливаясь либо тогда, когда у нас заканчиваются предки, либо когда мы находим тот, который установлен как невыбираемый.

Мое предположение о том, как работает выбор пользователя, может быть неверным; Может быть возможно заставить внутренний элемент быть доступным для выбора даже после установки предка не доступным для выбора. Логика может быть реорганизована, чтобы быть менее запутанной. Конечно, можно удалить рекурсию, используя вместо этого цикл. Массив userselect может использовать некоторый интеллект; Если это расширение, вы можете использовать его, чтобы сообщить, какие атрибуты вам нужно проверить. Этот код ожидает Элемент, а не Узел. На самом деле я не тестировал этот код, но похоже, что он должен работать.

0 голосов
/ 13 января 2019

Вот возможный путь без необходимости циклически проходить через предков узла:

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

Пояснение:
Если узел не выбирается пользователем, вы все равно можете выбрать его программно (selectAllChildren), но toString() все равно не будет включать текстовое содержимое узла.
В моем случае мне нужно перебрать все текстовые узлы document.body, и, к сожалению, это решение все еще слишком медленное для моей цели.

0 голосов
/ 12 января 2019

Ну, как я и подозревал, ваш код частично хорош (на 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...