理想的方法是使用 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/