在实践中,相对于innerHTML而言,使用createElement有何优势?我之所以问是因为,我坚信使用innerHTML在性能和代码可读性/可维护性方面会更有效,但是我的团队成员已经决定使用createElement作为编码方法。我只是想了解createElement如何更有效。
Answers:
除了安全之外,使用其他方法还具有一些优点,createElement
而不是修改innerHTML
(而不是仅仅丢弃已有的内容并替换它),例如Pekka已经提到的:
当您追加(或以其他方式修改)时innerHTML
,必须重新解析和重新创建该元素内的所有DOM节点。如果保存了对节点的任何引用,则它们实际上将是无用的,因为它们不再显示出来了。
这实际上只是最后一个的特例(尽管很常见)。设置innerHTML
不会自动将事件处理程序重新附加到它创建的新元素上,因此您必须自己跟踪它们并手动添加它们。在某些情况下,事件委托可以消除此问题。
如果您要进行大量添加操作,则绝对不希望继续进行重置,innerHTML
因为尽管对于简单的更改而言,速度更快,但是反复重新解析和创建元素会更慢。解决该问题的方法是在字符串中构建HTML,并innerHTML
在完成后设置一次。根据情况的不同,字符串操作可能比仅创建元素和附加元素要慢。
此外,字符串操作代码可能更复杂(特别是如果您希望它安全的话)。
这是我有时使用的功能,它使使用起来更加方便createElement
。
function isArray(a) {
return Object.prototype.toString.call(a) === "[object Array]";
}
function make(desc) {
if (!isArray(desc)) {
return make.call(this, Array.prototype.slice.call(arguments));
}
var name = desc[0];
var attributes = desc[1];
var el = document.createElement(name);
var start = 1;
if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) {
for (var attr in attributes) {
el[attr] = attributes[attr];
}
start = 2;
}
for (var i = start; i < desc.length; i++) {
if (isArray(desc[i])) {
el.appendChild(make(desc[i]));
}
else {
el.appendChild(document.createTextNode(desc[i]));
}
}
return el;
}
如果您这样称呼它:
make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);
您将获得与此HTML等效的代码:
<p>Here is a <a href="http://www.google.com/">link</a>.</p>
make
函数可以使事情变得更简单)。
innerHTML
由于其缺点,我通常会避免使用。对于复杂的标记(如构建表),我通常编写函数来生成标记的每个部分。例如,我将有一个函数tr
根据每一行的数据生成一个。然后,我可能会有另一个将行合并到表中的函数。每个函数都可以像make
使用适当参数进行调用一样简单。如果性能成为问题,我可以更改函数以返回HTML字符串。
虽然innerHTML
可能更快,但我不同意它在可读性或维护性方面更好。将所有内容放在一个字符串中可能会更短一些,但是更短的代码并不一定总是更具可维护性。
当需要创建动态DOM元素时,字符串串联只是无法缩放,因为加号和引号的开闭变得很难跟踪。考虑以下示例:
结果元素是一个具有两个内部跨度的div,其内容是动态的。第一个跨度内的一个类名(战士)也是动态的。
<div>
<span class="person warrior">John Doe</span>
<span class="time">30th May, 2010</span>
</div>
假设已经定义了以下变量:
var personClass = 'warrior';
var personName = 'John Doe';
var date = '30th May, 2010';
仅使用innerHTML并将所有内容混成一个字符串,我们得到:
someElement.innerHTML = "<div><span class='person " + personClass + "'>" + personName + "</span><span class='time'>" + date + "</span></div>";
可以使用字符串替换来清除上述混乱,以避免每次打开和关闭字符串。即使是简单的文本替换,我也更喜欢使用replace
而不是字符串连接。
这是一个简单的函数,它接受键和替换值的对象并将其替换为字符串。它假定键以前缀$
表示它们是一个特殊值。它不会进行任何转义或处理$
出现在替换值等中的边缘情况。
function replaceAll(string, map) {
for(key in map) {
string = string.replace("$" + key, map[key]);
}
return string;
}
var string = '<div><span class="person $type">$name</span><span class="time">$date</span></div>';
var html = replaceAll(string, {
type: personClass,
name: personName,
date: date
});
someElement.innerHTML = html;
可以通过在构造对象时分离属性,文本等来改进此功能,以对元素构造进行更多的编程控制。例如,使用MooTools,我们可以将对象属性作为地图传递。这当然是更易于维护的,而且我也会争辩说更具可读性。jQuery 1.4使用类似的语法来传递用于初始化DOM对象的映射。
var div = new Element('div');
var person = new Element('span', {
'class': 'person ' + personClass,
'text': personName
});
var when = new Element('span', {
'class': 'time',
'text': date
});
div.adopt([person, when]);
我不会说下面的纯DOM方法比上面的方法更具可读性,但是它肯定更具可维护性,因为我们不必跟踪开/关引号和大量加号。
var div = document.createElement('div');
var person = document.createElement('span');
person.className = 'person ' + personClass;
person.appendChild(document.createTextNode(personName));
var when = document.createElement('span');
when.className = 'date';
when.appendChild(document.createTextNode(date));
div.appendChild(person);
div.appendChild(when);
最易读的版本很可能是使用某种JavaScript模板生成的。
<div id="personTemplate">
<span class="person <%= type %>"><%= name %></span>
<span class="time"><%= date %></span>
</div>
var div = $("#personTemplate").create({
name: personName,
type: personClass,
date: date
});
var a = hello
,但我可以找出您的代码。这看起来比单个连接字符串更具可读性。
innerHTML
快点 ?不符合jsperf.com/innerhtml-vs-createelement-and-appendchild
用户bobince在对jQuery的评论中非常非常好地提出了许多缺点。
...此外,您可以通过说出$(''+ message +'')来创建div,而不必搞乱document.createElement('div')和文本节点。万岁!只是...等等。您尚未逃脱该HTML,并且可能只是在这次仅在客户端上创建了跨站点脚本安全漏洞。在花了这么长时间清理完PHP以便在服务器端使用htmlspecialchars之后。多可惜。嗯,没有人真正在乎正确性或安全性,对吗?
jQuery并不能完全怪罪于此。毕竟,innerHTML属性已经存在了很多年,并且已经证明比DOM更流行。但是库肯定会鼓励这种编码风格。
至于性能:InnerHTML肯定会变慢,因为需要对其进行解析并将其内部转换为DOM元素(可能使用该createElement
方法)。
根据@Pointy提供的quirksmode基准,InnerHTML在所有浏览器中的运行速度都更快。
至于可读性和易用性,你会发现我选择innerHTML
在createElement
一周的任何一天,大多数项目。但是正如您所看到的,有很多要说的要点createElement
。
innerHTML
要慢一点吗?我知道很长时间以来,这绝对是错误的。innerHTML
实际上,正是由于某些浏览器的显着性能优势(猜测是哪种),使用才真正在框架内流行。
createElement
确实在第一个步骤。但是,如果您知道有任何其他基准测试,我会很乐意得到纠正。
如果要在代码中保留引用,则应使用createElement。InnerHTML有时会创建难以发现的错误。
HTML代码:
<p id="parent">sample <span id='test'>text</span> about anything</p>
JS代码:
var test = document.getElementById("test");
test.style.color = "red"; //1 - it works
document.getElementById("parent").innerHTML += "whatever";
test.style.color = "green"; //2 - oooops
1)你可以改变颜色
2)您不能再更改颜色或其他任何内容,因为在上面的行中,您向innerHTML添加了一些内容,并且所有内容都被重新创建,并且您可以访问不再存在的内容。为了更改它,您必须再次getElementById。
您需要记住,它也会影响任何事件。您需要重新应用事件。
InnerHTML很棒,因为它速度更快,大多数时间更容易阅读,但是您必须小心并谨慎使用。如果您知道自己在做什么,就可以了。
模板文字(模板字符串)是另一种选择。
const container = document.getElementById("container");
const item_value = "some Value";
const item = `<div>${item_value}</div>`
container.innerHTML = item;