Как выделить выбранный пользователем текст, который не содержится в одном текстовом узле, используя vanilla js?

Я хочу получить выбор пользователя, а затем заменить их тегом метки на каждом текстовом узле внутри элемента, если пользователь выбрал два текстовых узла, которые являются дочерними, внуками (например: внутри тега, который является дочерним), поэтому окружите первый узел с <mark>first text node</mark> и второй вот так <mark><a>partial of second</a></mark><a>rest which is not highlighted</a>

<div>
<p id = "first" data-selectable-paragraph = "">
At the very first age, JavaScript was called LiveScript. An engineer named Brendan Eich has created JavaScript in 1995. There was a little confused about the name with Java and JavaScript. After several months Microsoft released JScript with Internet Explorer 3. After that Netscape submitted JavaScript to Ecma International. In 1999 ECMAScript edition 3 launched and it has stayed pretty much stable ever since.
</p>
<p>
Another Selection <span> you could do</span>
</p>
<p id = "second" data-selectable-paragraph = "">
Range is something I discovered recently, which again showed me that the possibilities with the Javascript and the DOM are truly endless. As stated on Mozilla’s developer site, range ‘represents a fragment of a document that can contain nodes and parts of text nodes’. So, if you create of select a section of a document, range could tell you the nodes that it contains, its starting and ending positions relative to the document, it can clone its content ,and much more. (Read more from the docs: 
<a href = "https://developer.mozilla.org/en-US/docs/Web/API/Range">
https://developer.mozilla.org/en-US/docs/Web/API/Range
</a>)
</p>
</div>

скажем, что пользователь выбрал 3 текста и выделил их, которые я хочу привести к этой структуре-> Краткое объяснение: если у родителя или дедушки и бабушки есть абзац, выбираемый данными, тогда выделите текстовые узлы внутри элемента и его родителя, если это внук

<div>
<p id = "first" data-selectable-paragraph>
At the very first age, JavaScript was called LiveScript. An engineer named Brendan Eich has created JavaScript in 1995. There was a little confusion about the name with Java and JavaScript. After several months Microsoft released JScript with Internet Explorer 3. After that Netscape submitted JavaScript to Ecma International. In 1999 ECMAScript edition 3 <mark>launched and it has stayed pretty much stable ever since.</mark>
</p>
<p class='second' data-selectable-paragraph>
<mark>Another Selection </mark><mark><span> you could do</span></mark>
</p>
<p id = "third" data-selectable-paragraph>
<mark>
Range is something I discovered recently, which again showed me that the possibilities with the Javascript and the DOM are truly endless. As stated on Mozilla’s developer site, range ‘represents a fragment of a document that can contain nodes and parts of text nodes’. So, if you create of select a section of a document, range could tell you the nodes that it contains, its starting and ending positions relative to the document, it can clone its content ,and much more. (Read more from the docs:
</mark>
<mark>
<a href = "https://developer.mozilla.org/en-US/docs/Web/API/Range">
https://developer.mozilla.org/en-US/docs/Web/API/Range
</a>
</mark>
<mark>)</mark>
</p>
</div>

то, что я нашел до сих пор, было window.getSelection().getRangeAt(0).commonAncestorContainer.hasAttribute("data-selectable-paragraph"); на мышке, но я не знаю, что делать дальше

Обновлено:

Выбранный текст

  1. запущен, и с тех пор он остается довольно стабильным. its parent is the first P.
  2. Еще одна подборка, которую вы могли бы сделать its parent is the second P.
  3. Range — это то, что я обнаружил недавно, и это снова показало мне, что возможности Javascript и DOM поистине безграничны. Как указано на сайте разработчиков Mozilla, диапазон «представляет собой фрагмент документа, который может содержать узлы и части текстовых узлов». Таким образом, если вы создаете или выбираете раздел документа, диапазон может сообщить вам содержащиеся в нем узлы, его начальную и конечную позиции относительно документа, он может клонировать его содержимое и многое другое. (Подробнее читайте в документации: https://developer.mozilla.org/en-US/docs/Web/API/Range) its parent is the third P.

Есть фрагменты до и после, поэтому, пожалуйста, примите их во внимание.

Я обрадовался, когда увидел surroundContents для Range объектов, но это не совсем подходит для этого сценария :( developer.mozilla.org/en-US/docs/Web/API/Range/surroundConte‌​nts

Kyle Pollard 17.12.2020 22:21

да, это не работает, если только это не текстовый узел.

Boudy hesham 18.12.2020 08:31

пожалуйста, добавьте более подробную информацию, например, что выбрал пользователь, что было текущим родителем и прародителем выбора, какие изменения происходят с выделенным текстом, родителем, прародителем,. и т. д.

Chandan 18.12.2020 17:31

@Chandan Я отредактировал его, но если бы вы посмотрели фрагмент до и после, вы бы его увидели.

Boudy hesham 19.12.2020 09:08

@Boudyhesham, я не мог понять, выбирает ли пользователь весь текст по одному или весь сразу

Chandan 19.12.2020 17:35

У вас есть только одна строка кода. window.getSelection().getRangeAt(0).commonAncestorContainer.‌​hasAttribute("data-s‌​electable-paragraph"‌​); И вы показываете это не в контексте. У вас есть полная страница, которую мы могли бы посмотреть?

fatman45 23.12.2020 21:58
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
6
512
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот начало. Я просто разрешаю вам пометить текст один раз, а не устанавливать и снимать отметки.

window.onload = function () {
    var btn = document.getElementsByClassName("get_selection")[0];
    btn.addEventListener("click", function () {
        var wrap = ["A"]; // nodes that should be wrapped in mark (rather than node's textContent wrapped in mark)
        var sel = window.getSelection();
        var range = sel.getRangeAt(0);

        if (!range.startContainer.isSameNode(range.endContainer)) {
            // get all nodes within the range commonAncestorContainer node
            var treeWalker = document.createTreeWalker(
                range.commonAncestorContainer,
                NodeFilter.SHOW_ALL
            );

            var nodeList = [];
            var currentNode = treeWalker.currentNode;
            while (currentNode) {
                nodeList.push(currentNode);
                currentNode = treeWalker.nextNode();
            }

            var start = null; // index that our selected nodes start
            var end = null; // index that our selected nodes end
            var selNodes = nodeList.filter(function (val, i) {
                // filter the node list
                var node = nodeList[i];
                start = start ?? (val.isSameNode(range.startContainer) ? i : null); // if same as start node
                end = end ?? (val.isSameNode(range.endContainer) ? i : null); // if same as end node
                var lesser = start == null || i <= start; // is before start node?
                var greater = end != null && i >= end; // is after end node?
                return (
                    !lesser &&
                    !greater &&
                    !node.isSameNode(range.endContainer.parentNode) && // node is not same as end node's parent
                    node != undefined &&
                    node != null &&
                    node.textContent.replace(/\t|\n/g, "") != "" &&
                    node.textContent.replace(/\t|\n/g, "") != undefined &&
                    !node.contains(range.endContainer) && // node does not contain end node
                    !node.isSameNode(range.endContainer.parentNode) // node is not same as end node's parent
                );
            });

            // mark node at start of selection
            var sParent = range.startContainer.parentNode;
            var sText = range.startContainer.textContent;
            var mark = document.createElement("mark");
            // wrap a tags in mark
            if (
                wrap.includes(sParent.nodeName) &&
                sText.replace(/\t+|\n+/gm, "") ==
                    sText.substring(range.startOffset).replace(/\t+|\n+/gm, "")
            ) {
                var node = sParent.cloneNode(true);
                mark.append(node);
                sParent.after(mark);
                sParent.remove();
            } else {
                mark.textContent = sText.substring(range.startOffset);
                range.startContainer.textContent = sText.substring(range.startOffset, -1);
                range.startContainer.after(mark);
            }

            // mark node at end of selection
            var eParent = range.endContainer.parentNode;
            // console.info("end parent: ", eParent);
            var eText = range.endContainer.textContent;
            var mark = document.createElement("mark");
            // wrap a tags in mark
            if (
                wrap.includes(eParent.nodeName) &&
                eText.replace(/\t+|\n+/gm, "") ==
                    eText.substring(range.endOffset, -1).replace(/\t+|\n+/gm, "")
            ) {
                var node = eParent.cloneNode(true);
                mark.append(node);
                eParent.after(mark);
                eParent.remove();
            } else {
                mark.textContent = eText.substring(range.endOffset, -1);
                range.endContainer.textContent = eText.substring(range.endOffset);
                range.endContainer.before(mark);
            }

            // mark nodes in between start and end
            selNodes.forEach(function (val, idx) {
                var currentNode = selNodes[idx];
                var mark = document.createElement("mark");
                if (currentNode.nodeType === Node.TEXT_NODE) {
                    // if text node, insert mark after node and remove node
                    mark.textContent = currentNode.textContent;
                    currentNode.after(mark);
                    currentNode.remove();
                } else {
                    if (wrap.includes(currentNode.nodeName)) {
                        var node = currentNode.cloneNode(true);
                        mark.append(node);
                        currentNode.after(mark);
                        currentNode.remove();
                    } else {
                        // reset the node's html and append mark
                        mark.textContent = currentNode.textContent;
                        currentNode.innerHTML = "";
                        currentNode.appendChild(mark);
                    }
                }
            });
        } else {
            var parentNode = range.startContainer.parentNode;
            var mark = document.createElement("mark");
            if (wrap.includes(parentNode.nodeName)) {
                var node = parentNode.cloneNode(true);
                node.textContent = sel.toString();
                mark.append(node);
                parentNode.after(mark);
                parentNode.remove();
            } else {
                var sText = document.createTextNode(
                    range.startContainer.textContent
                        .substring(range.startOffset, -1)
                        .toString()
                );
                var eText = document.createTextNode(
                    range.endContainer.textContent.substring(range.endOffset).toString()
                );
                mark.textContent = sel.toString();
                range.startContainer.after(eText);
                range.startContainer.after(mark);
                range.startContainer.after(sText);
                range.startContainer.remove();
            }
        }
    });
};
<button class = "get_selection">Set Markers</button>
<div>
  <p id = "first" data-selectable-paragraph>
    At the very first age, JavaScript was called LiveScript. An engineer named Brendan Eich has created JavaScript in 1995. There was a little confusion about the name with Java and JavaScript. After several months Microsoft released JScript with Internet
    Explorer 3. After that Netscape submitted JavaScript to Ecma International. In 1999 ECMAScript edition 3 launched and it has stayed pretty much stable ever since.
  </p>
  <p class='second' data-selectable-paragraph>
    Another Selection <span> you could do</span>
  </p>
  <p id = "third" data-selectable-paragraph>

    Range is something I discovered recently, which again showed me that the possibilities with the Javascript and the DOM are truly endless. As stated on Mozilla’s developer site, range ‘represents a fragment of a document that can contain nodes and parts
    of text nodes’. So, if you create of select a section of a document, range could tell you the nodes that it contains, its starting and ending positions relative to the document, it can clone its content ,and much more. (Read more from the docs:

    <a href = "https://developer.mozilla.org/en-US/docs/Web/API/Range">
            https://developer.mozilla.org/en-US/docs/Web/API/Range
        </a> )
  </p>
</div>

это то, что я искал, это не то, что я хочу на 100%, но это на 75% то, что я хочу, спасибо, щедрость закончилась, но я собираюсь возобновить ее снова, можете ли вы вкратце объяснить, как древолаз проходит через это, потому что я я никогда не имел дело с API-интерфейсом treeWalker, если вы выберете текстовый узел и текстовый узел тега привязки, вы пометите текст, который находится внутри тега привязки, но я ищу клонирование самого тега и размещение только выбранного текстового узла внутри якоря внутри тега метки это не обязательно, это необязательно, но если вы поможете мне с этим, я ценю это.

Boudy hesham 24.12.2020 06:44

@Boudyhesham Хорошо, если тег привязки выбран только частично, вы бы не хотели помечать все это, не так ли?

Souleste 25.12.2020 20:16

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

Boudy hesham 25.12.2020 21:54

и еще один комментарий, не связанный с этим, как вы узнали о treewalker, я имею в виду, что у меня 1 год опыта, и я никогда не имел возможности узнать об этом, можете ли вы сказать мне, есть ли хорошие книги, чтобы стать лучшим js-разработчиком, спасибо, и я очень ценю это.

Boudy hesham 25.12.2020 22:17

@Boudyhesham Честно говоря, просто изучение Интернета помогло мне найти его. Например, просмотр codepen и stackoverflow помогает вам находить новые вещи. Кстати, я использую MDN для документации, мне он нравится больше, чем школы ww3, просто он кажется мне более «собранным».

Souleste 25.12.2020 22:56

Можете ли вы кратко объяснить, как здесь работает treewalker.

Boudy hesham 26.12.2020 07:47

@Boudyhesham API-интерфейс treewalker позволяет вам получать узлы внутри указанного узла (range.commonAncestorContainer, который является общим предком начального узла выбора и конечного узла), вы можете использовать NodeFilter для фильтрации типов отображаемых узлов. Я использую show_all для получения всех типов узлов. Затем прокрутите узлы, используя цикл while. Пока treewalker.currentNode существует, поместите его в массив списка узлов и установите текущий узел как treewalker.nextNode, если следующий узел существует, вырваться. Вот документация: developer.mozilla.org/en-US/docs/Web/API/TreeWalker

Souleste 28.12.2020 17:44

@Boudyhesham Забыл упомянуть, что мой ответ и код были обновлены, чтобы обернуть теги a в тег mark, только когда выбран весь тег, я не совсем понимаю, что вы хотите, когда текст тега a выделен частично.

Souleste 02.01.2021 22:09

Обратите внимание, что вы удаляете остальную часть тега между прочим

Boudy hesham 03.01.2021 00:16

О, это потрясающе! Действительно то, что мне нужно! Сложно ли заставить запоминать выделенные места при перезагрузке страницы и удалять отмеченные при длительном нажатии? @Сулесте

RGS 12.04.2022 22:44

@RGS Скорее всего, вы можете хранить диапазоны с помощью файлов cookie. Чтобы удалить при длительном нажатии, вы можете посмотреть дочерние узлы родительского и предыдущего сиблинга, следующего сиблинга узла метки.

Souleste 13.04.2022 00:25

Другие вопросы по теме