Получить выделенный текст и выбранные узлы на странице?

При выборе блока текста (возможно, охватывающего многие узлы DOM) можно ли извлечь выделенный текст и узлы с помощью Javascript?

Представьте себе этот HTML-код:

<h1>Hello World</h1><p>Hi <b>there!</b></p>

Если пользователь инициировал событие mouseDown, начиная с «World ...», а затем mouseUp даже сразу после «there!», Я надеюсь, что оно вернется:

Text : { selectedText: "WorldHi there!" },
Nodes: [ 
  { node: "h1", offset: 6, length: 5 }, 
  { node: "p", offset: 0, length: 16 }, 
  { node: "p > b", offset: 0, length: 6 } 
]

Я пробовал поместить HTML в текстовое поле, но это даст мне только selectedText. Я не пробовал элемент <canvas>, но это может быть другим вариантом.

Если не JavaScript, возможно ли это с помощью расширения Firefox?

Поведение ключевого слова "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) для оценки ваших знаний,...
14
0
26 482
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Вы можете начать с http://javascript.internet.com/page-details/copy-selected-text.html

не отвечает на вопрос ни в малейшей степени.

Crazed'n'Dazed 04.08.2020 01:58
Ответ принят как подходящий

Вас ожидает ухабистая поездка, но это вполне возможно. Основная проблема заключается в том, что IE и W3C предоставляют совершенно разные интерфейсы для выбора, поэтому, если вам нужна кросс-браузерная функциональность, вам в основном придется писать все это дважды. Кроме того, в обоих интерфейсах отсутствуют некоторые базовые функции.

Связь разработчиков Mozilla рассказала о Выборы W3C. У Microsoft есть своя система задокументировано в MSDN. Я рекомендую начать с введение в диапазоны PPK.

Вот несколько основных функций, которые, как мне кажется, работают:

// selection objects will differ between browsers
function getSelection () {
  return ( msie ) 
    ? document.selection
    : ( window.getSelection || document.getSelection )();
}

// range objects will differ between browsers
function getRange () {
  return ( msie ) 
      ? getSelection().createRange()
      : getSelection().getRangeAt( 0 )
}

// abstract getting a parent container from a range
function parentContainer ( range ) {
  return ( msie )
      ? range.parentElement()
      : range.commonAncestorContainer;
}

что вы передаете parentContainer как r? У меня не получилось (метод parentContainer)

Khaled Al Hourani 10.06.2009 09:38

Ой. Не очень понятно, но это должен быть диапазон, я исправил имя переменной. Я думал, что это можно использовать так: var container = parentContainer (getRange ()); Нельзя сказать, что он будет работать на 100%. Код предназначен в качестве примера того типа работы, которая необходима для этого, и может быть сочтена недостойной. Вы захотите понять API, с которыми имеете дело (см. Ссылки).

Borgar 11.06.2009 04:48
parentContainer() бесполезен: две ветви не гарантируют возврата одного и того же, потому что метод parentElement() в IE TextRange всегда будет возвращать элемент, в то время как commonAncestorContainer может быть текстовым узлом. Кроме того, нет необходимости в каком-либо сниффинге браузера (что подразумевается при использовании msie): вы можете легко обнаружить нужные объекты и методы.
Tim Down 09.04.2012 20:15

Этот код мог вызвать переполнение стека (без каламбура). Взгляните на свою функцию getSelection; window.getSelection и getSelection - это одно и то же, поэтому, переопределив встроенный, вы больше не сможете получить доступ к встроенному getSelection, скорее, ваш getSelection вызывает сам себя.

Sapphire_Brick 24.05.2020 22:56

Есть гораздо более короткий способ, если вам просто нужен диапазон.

function getRange(){
    return (navigator.appName= = "Microsoft Internet Explorer")
        ? document.selection.createRange().parentElement()
        : (getSelection||document.getSelection)().getRangeAt(0).commonAncestorContainer
}

Это не идеально. Во-первых, анализ браузера бесполезен, поскольку IE 9 и более поздние версии поддерживают стандартные API-интерфейсы Selection и Range, и вы так же легко обнаруживаете нужные вам функции напрямую. Во-вторых, нет гарантии, что две ветви вернут одно и то же: метод parentElement() в IE TextRange всегда будет возвращать элемент, в то время как commonAncestorContainer может быть текстовым узлом. В-третьих, странное именование: функция возвращает узел, а не диапазон.

Tim Down 09.04.2012 20:11

Короче не обязательно лучше.

Stefan Falk 13.03.2016 23:16

Моя библиотека Стройный поможет вам в этом, объединив различные API-интерфейсы в IE <9 и всех других основных браузерах и предоставив функцию getNodes() для своих объектов Range:

function getSelectedNodes() {
    var selectedNodes = [];
    var sel = rangy.getSelection();
    for (var i = 0; i < sel.rangeCount; ++i) {
        selectedNodes = selectedNodes.concat( sel.getRangeAt(i).getNodes() );
    }
    return selectedNodes;
}

Получить выделенный текст довольно просто во всех браузерах. В Рэнги это просто

var selectedText = rangy.getSelection().toString();

Без Рэнги:

function getSelectedText() {
    var sel, text = "";
    if (window.getSelection) {
        text = "" + window.getSelection();
    } else if ( (sel = document.selection) && sel.type == "Text") {
        text = sel.createRange().text;
    }
    return text;
}

Что касается смещения символов, вы можете сделать что-то подобное для любого узла node в выборе. Обратите внимание, что это не обязательно представляет видимый текст в документе, потому что он не учитывает свернутые пробелы, текст, скрытый с помощью CSS, текст, расположенный за пределами обычного потока документа с помощью CSS, разрывы строк, подразумеваемые <br> и блочными элементами, а также другие тонкости.

var sel = rangy.getSelection();
var selRange = sel.getRangeAt(0);
var rangePrecedingNode = rangy.createRange();
rangePrecedingNode.setStart(selRange.startContainer, selRange.startOffset);
rangePrecedingNode.setEndBefore(node);
var startIndex = rangePrecedingNode.toString().length;
rangePrecedingNode.setEndAfter(node);
var endIndex = rangePrecedingNode.toString().length;
alert(startIndex + ", " + endIndex);

Здравствуйте, смотрю ваши работы. Это действительно лучший вариант. У меня все еще были проблемы с получением всего выделенного текста, разделенного запятыми. Могу я чем-нибудь помочь?

Sara Kat 20.09.2019 17:40

Тим, не могли бы вы дать мне какой-нибудь совет по поводу восстановления выделения на повторно отрисованном contenteditable div? Я задал вопрос об этом здесь stackoverflow.com/questions/61011651/…. Спасибо!

webprogrammer 03.04.2020 15:22

Это возвращает выбранные узлы, как я понимаю: Когда у меня есть

<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p>...
<p> ... </p><p> ... </p><p> ... </p><p> ... </p><p> ... </p>

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

function getSelectedNodes() {
  // from https://developer.mozilla.org/en-US/docs/Web/API/Selection
  var selection = window.getSelection();
  if (selection.isCollapsed) {
    return [];
  };
  var node1 = selection.anchorNode;
  var node2 = selection.focusNode;
  var selectionAncestor = get_common_ancestor(node1, node2);
  if (selectionAncestor == null) {
    return [];
  }
  return getNodesBetween(selectionAncestor, node1, node2);
}

function get_common_ancestor(a, b)
{
    // from http://stackoverflow.com/questions/3960843/how-to-find-the-nearest-common-ancestors-of-two-or-more-nodes
    $parentsa = $(a).parents();
    $parentsb = $(b).parents();

    var found = null;

    $parentsa.each(function() {
        var thisa = this;

        $parentsb.each(function() {
            if (thisa == this)
            {
                found = this;
                return false;
            }
        });

        if (found) return false;
    });

    return found;
}

function isDescendant(parent, child) {
     // from http://stackoverflow.com/questions/2234979/how-to-check-in-javascript-if-one-element-is-a-child-of-another
     var node = child;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}

function getNodesBetween(rootNode, node1, node2) {
  var resultNodes = [];
  var isBetweenNodes = false;
  for (var i = 0; i < rootNode.childNodes.length; i+= 1) {
    if (isDescendant(rootNode.childNodes[i], node1) || isDescendant(rootNode.childNodes[i], node2)) {
      if (resultNodes.length == 0) {
        isBetweenNodes = true;
      } else {
        isBetweenNodes = false;
      }
      resultNodes.push(rootNode.childNodes[i]);
    } else if (resultNodes.length == 0) {
    } else if (isBetweenNodes) {
      resultNodes.push(rootNode.childNodes[i]);
    } else {
      return resultNodes;
    }
  };
 if (resultNodes.length == 0) {
    return [rootNode];
  } else if (isDescendant(resultNodes[resultNodes.length - 1], node1) || isDescendant(resultNodes[resultNodes.length - 1], node2)) {
    return resultNodes;
  } else {
    // same child node for both should never happen
    return [resultNodes[0]];
  }
}

Код должен быть доступен по адресу: https://github.com/niccokunzmann/spiele-mit-kindern/blob/gh-pages/javascripts/feedback.js

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

Код, соответствующий всем стандартам, работающий в IE11 +.

Текстовая строка

window.getSelection().getRangeAt(0).toString()

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

window.getSelection().anchorNode

конечный узел (даже если текст выделен в обратном направлении):

window.getSelection().focusNode

Хочешь узнать больше? Выделите текст и запустите в консоли следующий код JavaScript:

console.info(window.getSelection());
console.info(window.getSelection().getRangeAt(0));

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