createElement优于innerHTML的优势?

oni*_*nea 74 javascript dom

在实践中,使用createElement而不是innerHTML有什么好处?我问,因为我确信使用innerHTML在性能和代码可读性/可维护性方面更有效但我的团队成员已经决定使用createElement作为编码方法.我只是想了解createElement如何更有效率.

Mat*_*ley 71

除了安全之外,使用createElement而不是修改innerHTML(而不是仅丢弃已经存在并替换它的东西)有几个优点,就像Pekka已经提到的那样:

在追加元素时保留对DOM元素的现有引用

当您追加(或以其他方式修改)时innerHTML,必须重新解析并重新创建该元素内的所有DOM节点.如果您保存了对节点的任何引用,它们将基本上无用,因为它们不再是那些出现的节点.

保留附加到任何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;
}
Run Code Online (Sandbox Code Playgroud)

如果你这样称呼它:

make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);
Run Code Online (Sandbox Code Playgroud)

你得到相当于这个HTML:

<p>Here is a <a href="http://www.google.com/">link</a>.</p>
Run Code Online (Sandbox Code Playgroud)

  • 还有一些速度优势,可以单独创建一个DOM树片段,然后在最后将它附加到真正的DOM上. (5认同)
  • 我通常避免使用“innerHTML”,因为它有缺点。对于复杂的标记(例如构建表格),我通常编写函数来生成标记的每个部分。例如,我有一个函数可以根据每行的数据生成“tr”。然后我可能有另一个将行组合到表中的函数。每个函数都可以像使用适当的参数调用“make”一样简单。如果性能成为问题,我可以更改函数以返回 HTML 字符串。 (2认同)

Anu*_*rag 15

虽然innerHTML可能更快,但我不同意它在可读性或维护方面更好.将所有内容放在一个字符串中可能会更短,但更短的代码并不总是更易于维护.

当需要创建动态DOM元素作为加号时,字符串连接不会缩放,并且引用开头和关闭变得难以跟踪.考虑这些例子:

结果元素是一个带有两个内跨的div,其内容是动态的.第一个范围内的一个类名(warrior)也是动态的.

<div>
    <span class="person warrior">John Doe</span>
    <span class="time">30th May, 2010</span>
</div>
Run Code Online (Sandbox Code Playgroud)

假设已经定义了以下变量:

var personClass = 'warrior';
var personName = 'John Doe';
var date = '30th May, 2010';
Run Code Online (Sandbox Code Playgroud)

使用innerHTML并将所有内容混合到一个字符串中,我们得到:

someElement.innerHTML = "<div><span class='person " + personClass + "'>" + personName + "</span><span class='time'>" + date + "</span></div>";
Run Code Online (Sandbox Code Playgroud)

使用字符串替换可以清除上述混乱,以避免每次打开和关闭字符串.即使是简单的文本替换,我更喜欢使用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;
Run Code Online (Sandbox Code Playgroud)

?这可以通过在构造对象时分离属性,文本等来改进,以获得对元素构造的更多编程控制.例如,使用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]);
Run Code Online (Sandbox Code Playgroud)

我不会将下面的纯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);
Run Code Online (Sandbox Code Playgroud)

最可读的版本很可能是使用某种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
});
Run Code Online (Sandbox Code Playgroud)


Pek*_*ica 12

用户bobince在他对jQuery的批评中非常非常好地提出了许多缺点.

...另外,你可以通过说$(''+ message +'')来制作div,而不必使用document.createElement('div')和文本节点.万岁!只...坚持下去.你没有逃过那个HTML,并且可能刚刚创建了一个跨站点脚本的安全漏洞,这次只在客户端.在你花了这么长时间清理你的PHP以在服务器端使用htmlspecialchars之后.多可惜.好吧,没有人真正关心正确性或安全性,是吗?

jQuery并不完全归咎于此.毕竟,innerHTML属性已存在多年,并且已经证明比DOM更受欢迎.但是图书馆确实鼓励这种编码风格.

至于性能:InnerHTML肯定会变慢,因为它需要被解析并在内部转换为DOM元素(可能使用该createElement方法).

根据@Pointy提供的quirksmode基准测试,InnerHTML 在所有浏览器中都更快.

至于可读性和易用性,您会发现我在大多数项目中选择innerHTMLcreateElement一周中的任何一天.但正如你所看到的,有许多观点可以说createElement.

  • 呃@Pekka 你真的确定 `innerHTML` 会变慢吗?我知道在很长一段时间里这绝对是错误的。事实上,使用“innerHTML”在框架内部变得流行,正是因为某些浏览器具有巨大的性能优势(猜猜是哪一个)。 (3认同)
  • 那么quirksmode基准测试有点旧:http://www.quirksmode.org/dom/innerhtml.html (2认同)
  • 感谢 Pekka 和 Pointy 的见解。这强化了我的观点:innerHTML 更快(除了编码经验之外)。此外,在数组中使用 html 字符串还可以提供额外的性能增益和可读性。我想知道 createElement 是否有任何使用它的要点。 (2认同)

Mor*_*don 7

如果要在代码中保留引用,则应使用createElement.InnerHTML有时会创建一个难以发现的错误.

HTML代码:

<p id="parent">sample <span id='test'>text</span> about anything</p>
Run Code Online (Sandbox Code Playgroud)

JS代码:

var test = document.getElementById("test");

test.style.color = "red"; //1 - it works

document.getElementById("parent").innerHTML += "whatever";

test.style.color = "green"; //2 - oooops
Run Code Online (Sandbox Code Playgroud)

1)你可以改变颜色

2)你不能改变颜色或其他任何东西,因为在上面的行中你向innerHTML添加了一些东西,所有东西都被重新创建,你可以访问不再存在的东西.为了改变它,你必须再次获得getElementById.

您需要记住它也会影响任何事件.您需要重新应用事件.

InnerHTML很棒,因为它更快,更容易阅读,但你必须小心谨慎使用它.如果你知道你在做什么,你就行了.