Преобразование диапазона или DocumentFragment в строку
Есть ли способ получить строку html объекта диапазона JavaScript в браузерах, совместимых с W3C?
Например, скажем, пользователь выбирает следующее: Hello <b>World</b>
В качестве строки можно использовать "Hello World", используя метод Range.toString()
.
(В Firefox это также возможно с помощью метода документа getSelection
.)
Но я не могу найти способ получить внутренний HTML.
После некоторого поиска я обнаружил, что диапазон может быть преобразован в объект DocumentFragment
.
Но DocumentFragments
не имеет свойства innerHTML
(по крайней мере, в Firefox, не пробовал Webkit или Opera).
Что мне кажется странным: было бы очевидно, что должен быть какой-то способ доступа к выбранным предметам.
Я понимаю, что я могу создать DocumentFragment
, добавить фрагмент документа к другому элементу, а затем получить innerHTML
этого элемента.
Но этот метод автоматически закрывает любые открытые теги в выбранной области.
Кроме того, существует очевидный "лучший способ", чем привязать его к dom, чтобы получить его как строку.
Итак, как получить строку html диапазона или DocFrag?
Ответы
Ответ 1
Нет, это единственный способ сделать это. Спецификации DOM Level 2 примерно с 10 лет назад почти ничего не означали с точки зрения сериализации и десериализации узлов в тексте HTML и из него, поэтому вам приходится полагаться на расширения, такие как innerHTML
.
Относительно вашего комментария, что
Но этот метод автоматически закрывает любые откройте теги в выбранной области.
... как еще он мог работать? DOM состоит из узлов, расположенных в дереве. Копирование содержимого из DOM может только создать другое дерево узлов. Узлы элементов ограничены в HTML начальным и иногда конечным тегом. HTML-представление элемента, для которого требуется конечный тег, должен иметь конечный тег, иначе он недействителен HTML.
Ответ 2
FWIW, способ jQuery:
$('<div>').append(fragment).html()
Ответ 3
Чтобы привести пример отсюда:
//Example setup of a fragment
var frag = document.createDocumentFragment(); //make your fragment
var p = document.createElement('p'); //create <p>test</p> DOM node
p.textContent = 'test';
frag.appendChild( p );
//Outputting the fragment content using a throwaway intermediary DOM element (div):
var div = document.createElement('div');
div.appendChild( frag.cloneNode(true) );
console.log(div.innerHTML); //output should be '<p>test</p>'
Ответ 4
Итак, как получить строку HTML диапазона или DocFrag?
В отличие от других ответов, можно напрямую превратить объект DocumentFragment
в DOMString
используя метод XMLSerializer.prototype.serializeToString
описанный по адресу https://w3c.github.io/DOM-Parsing/#the-xmlserializer-interface.
Чтобы получить DOMString
объекта Range
, просто преобразуйте его в DocumentFragment
используя один из методов Range.prototype.cloneContents
или Range.prototype.extractContents
а затем выполните процедуру для объекта DocumentFragment
.
Я приложил демо, но суть его в этих двух строках:
const serializer = new XMLSerializer();
const document_fragment_string = serializer.serializeToString(document_fragment);
(() => {
"use strict";
const HTML_namespace = "http://www.w3.org/1999/xhtml";
document.addEventListener("DOMContentLoaded", () => {
/* Create Hypothetical User Range: */
const selection = document.defaultView.getSelection();
const user_range_paragraph = document.getElementById("paragraph");
const user_range = document.createRange();
user_range.setStart(user_range_paragraph.firstChild, 0);
user_range.setEnd(user_range_paragraph.lastChild, user_range_paragraph.lastChild.length || user_range_paragraph.lastChild.childNodes.length);
selection.addRange(user_range);
/* Clone Hypothetical User Range: */
user_range.setStart(selection.anchorNode, selection.anchorOffset);
user_range.setEnd(selection.focusNode, selection.focusOffset);
const document_fragment = user_range.cloneContents();
/* Serialize the User Range to a String: */
const serializer = new XMLSerializer();
const document_fragment_string = serializer.serializeToString(document_fragment);
/* Output the Serialized User Range: */
const output_paragraph = document.createElementNS(HTML_namespace, "p");
const output_paragraph_code = document.createElementNS(HTML_namespace, "code");
output_paragraph_code.append(document_fragment_string);
output_paragraph.append(output_paragraph_code);
document.body.append(output_paragraph);
}, { "once": true });
})();
<p id="paragraph">Hello <b>World</b></p>
Ответ 5
Может ли DocumentFragment.textContent дать вам то, что вам нужно?
var frag = document.createRange().createContextualFragment("Hello <b>World</b>.");
console.log(frag.textContent)
Ответ 6
Еще один способ сделать это - перебрать childNodes:
Array.prototype.reduce.call(
documentFragment.childNodes,
(result, node) => result + (node.outerHTML || node.nodeValue),
''
);
Не будет работать для встроенного SVG, но что-то можно сделать, чтобы заставить его работать. Это также помогает, если вам нужно объединить некоторые цепочки с узлами и получить в результате HTML-строку.