将 Range 或 DocumentFragment 转换为字符串

发布于 2024-10-15 21:01:53 字数 701 浏览 8 评论 0原文

有没有办法在 W3C 兼容的浏览器中获取 JavaScript Range 对象的 html 字符串?

例如,假设用户选择以下内容:Hello World
可以使用 Range.toString() 方法以字符串形式获取“Hello World”。 (在 Firefox 中,也可以使用 documentgetSelection 方法。)

但我似乎找不到获取内部 HTML 的方法。

经过一番搜索,我发现范围可以转换为 DocumentFragment 对象。

DocumentFragments 没有 innerHTML 属性(至少在 Firefox 中;尚未尝试过 Webkit 或 Opera)。
这对我来说似乎很奇怪:显然应该有某种方法来访问所选项目。

我意识到我可以创建一个 documentFragment,将文档片段附加到另一个元素,然后获取该元素的 innerHTML
但该方法将自动关闭我选择的区域内所有打开的标签。
除此之外,肯定有一个明显的“更好的方法”,而不是将其附加到 dom 只是为了将其作为字符串获取。

那么,如何获取Range或DocFrag的html字符串呢?

Is there a way to get the html string of a JavaScript Range Object in W3C compliant browsers?

For example, let us say the user selects the following: Hello <b>World</b>
It is possible to get "Hello World" as a string using the Range.toString() method.
(In Firefox, it is also possible using the document's getSelection method.)

But I can't seem to find a way to get the inner HTML.

After some searching, I've found that the range can be converted to a DocumentFragment Object.

But DocumentFragments have no innerHTML property (at least in Firefox; have not tried Webkit or Opera).
Which seems odd to me: It would seem obvious that there should be some way to acces the selected items.

I realize that I can create a documentFragment, append the document fragment to another element, and then get the innerHTML of that element.
But that method will auto close any open tags within the area I select.
Besides that there surely is an obvious "better way" than attaching it to the dom just to get it as a string.

So, how to get the string of the html of a Range or DocFrag?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

︶ ̄淡然 2024-10-22 21:01:53

那么,如何获取Range或DocFrag的html字符串呢?

与其他响应相反,可以使用 XMLSerializer.prototype.serializeToString< 将 DocumentFragment 对象直接转换为 DOMString /code> 方法描述于 https://w3c.github.io/DOM-解析/#the-xmlserializer-interface

要获取 Range 对象的 DOMString,只需使用 Range.prototype.cloneContentsDocumentFragment code> 或 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>

So, how to get the string of the html of a Range or DocFrag?

Contrary to the other responses, it is possible to directly turn a DocumentFragment object into a DOMString using the XMLSerializer.prototype.serializeToString method described at https://w3c.github.io/DOM-Parsing/#the-xmlserializer-interface.

To get the DOMString of a Range object, simply convert it to a DocumentFragment using either of the Range.prototype.cloneContents or Range.prototype.extractContents methods and then follow the procedure for a DocumentFragment object.

I've attached a demo, but the gist of it is in these two lines:

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>

豆芽 2024-10-22 21:01:53

FWIW,jQuery 方式:

$('<div>').append(fragment).html()

FWIW, the jQuery way:

$('<div>').append(fragment).html()
夏天碎花小短裙 2024-10-22 21:01:53

详细说明此处的示例:

//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>'

To spell out an example from here:

//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>'
挽梦忆笙歌 2024-10-22 21:01:53

另一种方法是迭代 childNodes:

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

对于内联 SVG 不起作用,但可以采取一些措施让它工作。如果您需要对节点进行一些链式操作并获得 html 字符串作为结果,它也会有所帮助。

Another way to do it would be to iterate over childNodes:

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

Wouldn't work for inlined SVG, but something could be done to get it to work. It also helps if you need to do some chained manipulation with the nodes and get an html string as a result.

迷乱花海 2024-10-22 21:01:53

不,这是唯一的方法。大约 10 年前的 DOM Level 2 规范在节点与 HTML 文本之间的序列化和反序列化方面几乎没有任何内容,因此您被迫依赖像 innerHTML 这样的扩展。

关于你的评论

但是该方法会自动关闭任何
在我选择的区域内打开标签。

……不然还能怎么用呢? DOM 由排列在树中的节点组成。从 DOM 复制内容只能创建另一个节点树。在 HTML 中,元素节点由开始标记(有时也由结束标记)分隔。需要结束标记的元素的 HTML 表示形式必须具有结束标记,否则它不是有效的 HTML。

No, that is the only way of doing it. The DOM Level 2 specs from around 10 years ago had almost nothing in terms of serializing and deserializing nodes to and from HTML text, so you're forced to rely on extensions like innerHTML.

Regarding your comment that

But that method will auto close any
open tags within the area I select.

... how else could it work? The DOM is made up of nodes arranged in a tree. Copying content from the DOM can only create another tree of nodes. Element nodes are delimited in HTML by a start and sometimes an end tag. An HTML representation of an element that requires an end tag must have an end tag, otherwise it is not valid HTML.

乙白 2024-10-22 21:01:53

DocumentFragment.textContent 能否满足您的需求?

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

console.log(frag.textContent)

Could DocumentFragment.textContent give you what you need?

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

console.log(frag.textContent)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文