将 Range 或 DocumentFragment 转换为字符串

问题描述 投票:0回答:6

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

例如,假设用户选择以下内容:

Hello <b>World</b>

可以使用
Range.toString()
方法获取字符串形式的“Hello World”。 (在 Firefox 中,也可以使用 document
getSelection
方法。)

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

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

DocumentFragment
对象。

但是

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

我意识到我可以创建一个

documentFragment
,将文档片段附加到另一个元素,然后获取该元素的
innerHTML

但该方法会自动关闭我选择的区域内所有打开的标签。
除此之外,肯定有一个明显的“更好的方法”,而不是将其附加到 dom 只是为了将其作为字符串获取。

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

javascript selection range tostring documentfragment
6个回答
20
投票

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

与其他响应相反,可以使用 https://w3c.github.io/DOM-Parsing/#the 中描述的

DocumentFragment
方法直接将
DOMString
对象转换为
XMLSerializer.prototype.serializeToString
。 -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>


18
投票
FWIW,jQuery 方式:

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

16
投票

这里拼出一个例子:

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

7
投票
另一种方法是迭代子节点:

Array.prototype.reduce.call( documentFragment.childNodes, (result, node) => result + (node.outerHTML || node.nodeValue), '' );
不适用于内联 SVG,但可以采取一些措施让它工作。如果您需要对节点进行一些链式操作并获得 html 字符串作为结果,它也会有所帮助。


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

innerHTML

 这样的扩展。

关于您的评论

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

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


-1
投票
DocumentFragment.textContent 能否满足您的需求?

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

© www.soinside.com 2019 - 2024. All rights reserved.