为什么“element.innerHTML+=”是糟糕的代码?

IT技术 javascript html innerhtml anti-patterns
2021-02-05 05:42:15

我被告知不要使用element.innerHTML += ...这样的方式附加内容

var str = "<div>hello world</div>";
var elm = document.getElementById("targetID");

elm.innerHTML += str; //not a good idea?

它有什么问题?,我还有什么其他选择?

6个回答

每次innerHTML设置时,都必须解析 HTML、构造 DOM 并将其插入到文档中。这需要时间。

例如,如果elm.innerHTML有数千个 div、表格、列表、图像等,那么调用.innerHTML += ...将导致解析器重新解析所有这些内容这也可能会破坏对已构建的 DOM 元素的引用并导致其他混乱。实际上,您要做的就是在末尾添加一个新元素。

最好只调用appendChild

var newElement = document.createElement('div');
newElement.innerHTML = '<div>Hello World!</div>';
elm.appendChild(newElement);​​​​​​​​​​​​​​​​

这样,elm就不会再次解析的现有内容

注意: [某些] 浏览器可能足够智能以优化+=操作符而不重新解析现有内容。我没有研究过这个。

做就是了 var s = elem.innerHTML
2021-03-14 05:42:15
在这个例子中,他只设置了innerHTML一次,所以我不确定这能回答这个问题。
2021-03-15 05:42:15
不要忘记它还会销毁被销毁节点上的任何有状态数据。这包括事件处理程序。
2021-03-20 05:42:15
但是如果目标已经有一些我们想要保留的重要内容(例如附加到正文)怎么办?,这将删除它
2021-03-31 05:42:15
好吧,说里面innerHTML有 200,000 字节的东西。然后,您附加一个<div>Hello</div>. 现在,您要重新解析 200,000 字节加上单个 DIV 并重新构建整个 DOM。只调用appendChild()一次并直接插入现有 DOM会好得多
2021-04-04 05:42:15

是的,这elm.innerHTML += str;是一个非常糟糕的主意。
使用elm.insertAdjacentHTML( 'beforeend', str )的完美替代品。

典型的“浏览器必须重建 DOM”的答案确实没有解决问题:

  1. 首先,浏览器需要遍历 elm 下的每个元素、它们的每个属性以及它们的所有文本、注释和流程节点,并将它们转义以构建一个字符串。

  2. 然后你有一个长字符串,你附加到它。这一步没问题。

  3. 第三,当您设置innerHTML 时,浏览器必须删除它刚刚通过的所有元素、属性和节点。

  4. 然后它解析字符串,从它刚刚销毁的所有元素、属性和节点构建,以创建一个几乎相同的新 DOM 片段。

  5. 最后它附加新节点,浏览器必须布局整个事情。这可能是可以避免的(请参阅下面的替代方案),但即使附加节点需要布局,旧节点也会缓存其布局属性,而不是从新节点重新计算。

  6. 但它还没有完成!浏览器还必须通过扫描所有javascript 变量来回收旧节点

问题:

  • 某些属性可能不会被 HTML 反映,例如 的当前值<input>将丢失并重置为 HTML 中的初始值。

  • 如果旧节点上有任何事件处理程序,它们将被销毁,您必须重新附加所有这些事件处理程序。

  • 如果您的 js 代码引用了任何旧节点,它们不会被销毁,而是会被孤立。它们属于文档但不再在 DOM 树中。当您的代码访问它们时,可能什么也不会发生,也可能会引发错误。

  • 这两个问题都意味着它对 js 插件不友好——插件可能会附加处理程序,或者保留对旧节点的引用并导致内存泄漏。

  • 如果您养成了使用 innerHTML 进行 DOM 操作的习惯,您可能会不小心更改属性或做其他您不想做的事情。

  • 您拥有的节点越多,效率越低,电池电量就越多。

简而言之,它效率低下,容易出错,它只是懒惰和不知情。


最好的选择是Element.insertAdjacentHTML,我还没有看到其他答案提到:

elm.insertAdjacentHTML( 'beforeend', str )

几乎相同的代码,没有innerHTML 的问题。没有重建,没有处理程序丢失,没有输入重置,更少的内存碎片,没有坏习惯,没有手动元素创建和分配。

它允许您将 html 字符串注入一行中的元素,包括属性,甚至允许 yow 注入复合元素和多个元素。它的速度得到了优化——在 Mozilla 的测试中,它的速度提高了150倍。

如果有人告诉你它不是跨浏览器,它非常有用,它是 HTML5标准并且可用于所有浏览器

永远不要再写elm.innerHTML+=了。

如今,innerHTML 和insertAdjacentHTML 都被认为是一个坏主意。建议使用 DOMParser 对象。
2021-03-12 05:42:15
@VitalyZdanevich 如果我在 google 中发现类似问题有任何迹象,则警告应该出现在您的输入中,即使您设置innerHTML而不是调用insertAdjacentHTML.
2021-03-19 05:42:15
@RahulDesai 这个问题是关于插入的,真的。对于完全替换,有时我 removeChild & appendChild / insertAdjacement,有时用 createElement / createContextualFragment 替换 Node。
2021-03-30 05:42:15
在向 Mozilla 商店发布扩展时,我收到警告: Unsafe call to insertAdjacentHTML Warning: Due to both security and performance concerns, this may not be set using dynamic values which have not been adequately sanitized. This can lead to security issues or fairly serious performance degradation.
2021-04-08 05:42:15
+1 用于突出.innerHTML. 我不太相信Element.insertAdjacentHTML作为替代方案。.innerHTML有助于替换元素内的所有内容,但Element.insertAdjacentHTML与插入有关,而不是替换。
2021-04-10 05:42:15

短的

如果您将innerHTML += ...(更新内容)更改innerHTML = ...(重新生成内容),那么您将获得非常快的代码看起来最慢的部分+=是将 DOM 内容作为字符串读取(而不是将字符串转换为 DOM)

使用的缺点innerHTML是你失去了旧的内容事件处理程序 - 但是你可以使用标签参数来省略这个,例如<div onclick="yourfunc(event)">在小项目中可以接受的

我做了性能测试,这里在Chrome,Firefox和Safari(2019日)(你可以在你的机器上运行它们,但要有耐心-它需要约5分钟)

在此处输入图片说明


  • 对于所有浏览器,这innerHTML +=是最慢的解决方案。
  • chrome 最快的解决方案appendChild- 它比第二个快速解决方案快约 38%,但它非常不方便。令人惊讶的是,在 FirefoxappendChild上比innerHTML =.
  • 第二个快速的解决方案和类似的性能,我们得到insertAdjacentHTML strinnerHTML = str
  • 如果我们仔细观察案例innerHTML = innerHTML +str并与innerHTML = str进行比较,看起来最慢的部分innerHTML +=是将DOM 内容作为字符串读取(而不是将字符串转换为 DOM)
  • 如果你想改变 DOM 树生成第一个完整的字符串(带有 html)并且只更新/重新生成 DOM一次
  • 混合appendChildinnerHTML=实际上比纯慢innerHTML=

替代方案是.createElement().textContent、 和.appendChild()+=如果您要处理大量数据,附加 with只是一个问题。

演示: http : //jsfiddle.net/ThinkingStiff/v6WgG/

脚本

var elm = document.getElementById( 'targetID' ),
    div = document.createElement( 'div' );
div.textContent = 'goodbye world';
elm.appendChild( div );

HTML

<div id="targetID">hello world</div>

如果用户使用的是旧版本的 IE(或者也可能是新版本,还没有尝试过),a 上的 innerHTMLtd会导致问题。IE中的表格元素是只读的,tsk tsk tsk。