Преобразование Range или DocumentFragment в строку - PullRequest
20 голосов
/ 04 февраля 2011

Есть ли способ получить html-строку JavaScript Range Object в W3C-совместимых браузерах?

Например, предположим, что пользователь выбирает следующее: Hello <b>World</b>
Возможнополучить "Hello World" в виде строки, используя метод Range.toString().(В Firefox это также возможно с использованием метода document getSelection.)

Но я не могу найти способ получить внутренний HTML.

После некоторых поисков я обнаружил, что диапазон можно преобразовать в DocumentFragment объект.

Но DocumentFragments не имеет свойства innerHTML (по крайней мере в Firefox; не пробовал Webkitили Opera).
Что мне кажется странным: Казалось бы, очевидно, что должен быть какой-то способ доступа к выбранным элементам.

Я понимаю, что могу создать documentFragment, добавить фрагмент документак другому элементу, а затем получите innerHTML этого элемента.
Но этот метод автоматически закроет все открытые теги в области, которую я выберу.
Кроме того, несомненно, существует очевидный «лучший способ», чем его присоединение.в дом, просто чтобы получить его в виде строки.

Итак, как получить строку HTML или Range или DocFrag?

Ответы [ 6 ]

12 голосов
/ 17 июля 2014

FWIW, способ jQuery:

$('<div>').append(fragment).html()
9 голосов
/ 10 июля 2014

Разобрать пример из здесь :

//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>'
5 голосов
/ 18 апреля 2018

Итак, как получить строку html для Range или 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>
4 голосов
/ 04 февраля 2011

Нет, это единственный способ сделать это. Спецификации DOM Level 2, появившиеся около 10 лет назад, практически не имели ничего общего с сериализацией и десериализацией узлов в и из текста HTML, поэтому вы вынуждены полагаться на такие расширения, как innerHTML.

Относительно вашего комментария, что

Но этот метод автоматически закроет любой открыть теги в выбранной области.

... как еще это может работать? DOM состоит из узлов, расположенных в дереве. Копирование содержимого из DOM может только создать другое дерево узлов. Узлы элементов ограничиваются в HTML начальным, а иногда и конечным тегом. HTML-представление элемента, для которого требуется конечный тег, должно иметь конечный тег, в противном случае это недопустимый HTML.

0 голосов
/ 01 февраля 2019

Еще один способ сделать это - выполнить итерации по дочерним узлам:

Array.prototype.reduce.call(
    documentFragment.childNodes, 
    (result, node) => result + (node.outerHTML || node.nodeValue),
    ''
);

Не будет работать для встроенного SVG, но что-то можно сделать, чтобы заставить его работать.Это также помогает, если вам нужно объединить некоторые цепочки с узлами и получить в результате HTML-строку.

0 голосов
/ 31 августа 2018

Может ли DocumentFragment.textContent дать вам то, что вам нужно?

var frag = document.createRange().createContextualFragment("Hello <b>World</b>.");

console.log(frag.textContent)
...