是否有任何方法可以从通用的HTML片段创建文档片段?
Is there any way to create a document fragment from a generic piece of HTML?
我正在开发一个应用程序,该应用程序使用一些客户端模板来呈现数据,大多数javascript模板引擎返回带有标记的简单字符串,并由开发人员将该字符串插入DOM。
我搜索了一下,看到很多人建议使用空div,将其innerHTML设置为新字符串然后遍历该div的子节点,像这样
var parsedTemplate = 'something returned by template engine';
var tempDiv = document.createElement('div'), childNode;
var documentFragment = document.createDocumentFragment;
tempDiv.innerHTML = parsedTemplate;
while ( childNode = tempDiv.firstChild ) {
documentFragment.appendChild(childNode);
}
和TADA, documentFragment
现在包含解析的模板。然而,如果我的模板恰好是一个tr
,在它周围添加div并没有达到预期的行为,因为它添加了td
的内容在行内。
有人知道解决这个问题的好方法吗?现在,我正在检查将插入解析模板的节点,并从其标记名称创建元素。我甚至不确定是否还有其他方法可以做到这一点。
在搜索的过程中,我在w3邮件列表中看到了这个讨论,但不幸的是,没有有用的解决方案。
您可以使用DOMParser作为XHTML来避免dom喜欢执行的HTML "自动更正":
var parser = new DOMParser(),
doc = parser.parseFromString('<tr><td>something returned </td><td>by template engine</td></tr>', "text/xml"),
documentFragment = document.createDocumentFragment() ;
documentFragment.appendChild( doc.documentElement );
//fragment populated, now view as HTML to verify fragment contains what's expected:
var temp=document.createElement('div');
temp.appendChild(documentFragment);
console.log(temp.outerHTML);
// shows: "<div><tr><td>something returned </td><td>by template engine</td></tr></div>"
与使用朴素的innerHTML和临时div:
形成对比var temp=document.createElement('div');
temp.innerHTML='<tr><td>something returned </td><td>by template engine</td></tr>';
console.log(temp.outerHTML);
// shows: '<div>something returned by template engine</div>' (bad)
通过将模板处理为XHTML/XML(确保其格式良好),我们可以改变HTML的常规规则。DOMParser的覆盖范围应该与对documentFragment的支持相关,但是在firefox的一些旧版本(个位数版本)上,您可能需要使用importNode()。
作为可重用的函数:
function strToFrag(strHTML){
var temp=document.createElement('template');
if( temp.content ){
temp.innerHTML=strHTML;
return temp.content;
}
var parser = new DOMParser(),
doc = parser.parseFromString(strHTML, "text/xml"),
documentFragment = document.createDocumentFragment() ;
documentFragment.appendChild( doc.documentElement );
return documentFragment;
}
这是一个未来,而不是现在,但HTML5定义了一个<template>
元素,将为您创建片段。你可以这样做:
var parsedTemplate = '<tr><td>xxx</td></tr>';
var tempEL = document.createElement('template');
tempEl.innerHTML = parsedTemplate;
var documentFragment = tempEl.content;
它目前在Firefox中工作。看到
理想的方法是使用来自HTML5的<template>
标签。您可以以编程方式创建模板元素,将.innerHTML
分配给它,所有解析的元素(甚至是表的片段)都将出现在template.content
属性中。这已经帮你搞定了。但是,这只存在于最新版本的Firefox和Chrome中。
如果存在模板支持,就像这样简单:
function makeDocFragment(htmlString) {
var container = document.createElement("template");
container.innerHTML = htmlString;
return container.content;
}
返回的结果就像documentFragment
一样。你可以直接附加它,它就像documentFragment
一样解决了问题,除了它有支持.innerHTML
赋值的优势,它允许你使用部分形成的HTML片段(解决了我们需要的两个问题)。
但是,模板支持还不是无处不在,所以你需要一个后备方法。处理回退的蛮力方法是偷看HTML字符串的开头,查看它以什么类型的选项卡开始,并为该类型的标记创建适当的容器,并使用该容器将HTML分配给它。这是一种蛮力方法,但它有效。对于只能合法存在于特定类型容器中的任何类型的HTML元素,都需要这种特殊处理。我在下面的代码中包含了一堆这些类型的元素(尽管我没有试图使列表详尽无遗)。下面是代码和一个工作的jsFiddle链接。如果您使用的是最新版本的Chrome或Firefox,则代码将采用使用模板对象的路径。如果是其他浏览器,它会创建相应类型的容器对象。
var makeDocFragment = (function() {
// static data in closure so it only has to be parsed once
var specials = {
td: {
parentElement: "table",
starterHTML: "<tbody><tr class='xx_Root_'></tr></tbody>"
},
tr: {
parentElement: "table",
starterHTML: "<tbody class='xx_Root_'></tbody>"
},
thead: {
parentElement: "table",
starterHTML: "<tbody class='xx_Root_'></tbody>"
},
caption: {
parentElement: "table",
starterHTML: "<tbody class='xx_Root_'></tbody>"
},
li: {
parentElement: "ul",
},
dd: {
parentElement: "dl",
},
dt: {
parentElement: "dl",
},
optgroup: {
parentElement: "select",
},
option: {
parentElement: "select",
}
};
// feature detect template tag support and use simpler path if so
// testing for the content property is suggested by MDN
var testTemplate = document.createElement("template");
if ("content" in testTemplate) {
return function(htmlString) {
var container = document.createElement("template");
container.innerHTML = htmlString;
return container.content;
}
} else {
return function(htmlString) {
var specialInfo, container, root, tagMatch,
documentFragment;
// can't use template tag, so lets mini-parse the first HTML tag
// to discern if it needs a special container
tagMatch = htmlString.match(/^'s*<([^>'s]+)/);
if (tagMatch) {
specialInfo = specials[tagMatch[1].toLowerCase()];
if (specialInfo) {
container = document.createElement(specialInfo.parentElement);
if (specialInfo.starterHTML) {
container.innerHTML = specialInfo.starterHTML;
}
root = container.querySelector(".xx_Root_");
if (!root) {
root = container;
}
root.innerHTML = htmlString;
}
}
if (!container) {
container = document.createElement("div");
container.innerHTML = htmlString;
root = container;
}
documentFragment = document.createDocumentFragment();
// start at the actual root we want
while (root.firstChild) {
documentFragment.appendChild(root.firstChild);
}
return documentFragment;
}
}
// don't let the feature test template object hang around in closure
testTemplate = null;
})();
// test cases
var frag = makeDocFragment("<tr><td>Three</td><td>Four</td></tr>");
document.getElementById("myTableBody").appendChild(frag);
frag = makeDocFragment("<td>Zero</td><td>Zero</td>");
document.getElementById("emptyRow").appendChild(frag);
frag = makeDocFragment("<li>Two</li><li>Three</li>");
document.getElementById("myUL").appendChild(frag);
frag = makeDocFragment("<option>Second Option</option><option>Third Option</option>");
document.getElementById("mySelect").appendChild(frag);
带有几个测试用例的工作演示:http://jsfiddle.net/jfriend00/SycL6/
使用此函数
- 支持IE11
- 不必符合xml。"& lt; td hidden>测试"
function createFragment(html){
var tmpl = document.createElement('template');
tmpl.innerHTML = html;
if (tmpl.content == void 0){ // ie11
var fragment = document.createDocumentFragment();
var isTableEl = /^[^'S]*?<(t(?:head|body|foot|r|d|h))/i.test(html);
tmpl.innerHTML = isTableEl ? '<table>'+html : html;
var els = isTableEl ? tmpl.querySelector(RegExp.$1).parentNode.childNodes : tmpl.childNodes;
while(els[0]) fragment.appendChild(els[0]);
return fragment;
}
return tmpl.content;
}
@dandavis的解决方案将只接受ie11中符合xml的内容。
我不知道是否还有其他的标签需要考虑?
- 具有服务器端呈现的HTML片段的主干模型
- 我的html表单无法验证.请参阅代码片段中的html代码和java脚本
- 将代码片段转换为 HTML 页面
- KnockoutJS-打印html片段
- 如何将 HTML 字符串附加到文档片段
- 使用 AngularJS 解析 HTML 片段
- 使用 Javascript 或所见即所得打开和编辑 html 页面的片段
- 保留粘贴到 IE 中的 HTML 片段的条件注释
- 如何使用 JQuery 从另一个文件加载 HTML 片段
- Fancybox:如何滚动HTML片段,而不是具有相同效果的图像
- 如何重用HTML片段,但更改其中元素的ID
- 在libxmljs中附加html片段作为节点值
- 为单页应用程序构建HTML和JS片段的时组装
- 如何将HTML片段转换为另一个HTML片段
- 使用jQuery查找离滚动位置最近的HTML片段标识符
- Backbone.js-如何包含外部HTML片段
- 如何为拥有它的HTML片段提供初始数据'自己的控制器
- 我可以将HTML片段存储在不同的文件中,但可以通过JavaScript访问吗
- 如何存储HTML片段并稍后将其插入文档中
- 正在为HTML片段中的img标记设置baseUrl