为什么"element.innerHTML + ="代码不好?

aja*_*221 46 html javascript anti-patterns innerhtml

我被告知不要使用element.innerHTML += ...这样的东西来附加东西:

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

elm.innerHTML += str; //not a good idea?
Run Code Online (Sandbox Code Playgroud)

这有什么问题?我还有其他选择吗?

Mik*_*sen 39

每次innerHTML设置,都必须解析HTML,构建DOM并插入到文档中.这需要时间.

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

最好只打电话appendChild:

var newElement = document.createElement('div');
newElement.innerHTML = '<div>Hello World!</div>';
elm.appendChild(newElement);????????????????
Run Code Online (Sandbox Code Playgroud)

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

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

  • 在这个例子中,他只设置了一次`innerHTML`,所以我不确定这回答了这个问题. (3认同)
  • 好吧说`innerHTML`里面有200,000字节的东西.然后,你追加一个`<div> Hello </ div>`.现在,您将重新解析200,000个字节加上单个DIV并重新构建整个DOM.只需调用`appendChild()`一次并直接插入现有DOM就好了. (2认同)
  • 嗯......你把一个div放在一个段落里面.这不是一个有效的HTML. (2认同)

She*_*epy 23

是的,这elm.innerHTML += str;是一个非常糟糕的主意.

典型的"浏览器必须重建DOM"答案真的不公平:

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

  2. 然后你有一个长串,你追加到.这一步没问题.

  3. 第三,当你设置innerHTML时,浏览器必须删除它刚刚经历的所有元素,属性和节点.

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

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

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

问题:

  • HTML可能无法反映某些属性,例如,当前值elm.insertAdjacentHTML( 'beforeend', str )将丢失并重置为HTML中的初始值.

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

  • 如果您的js代码引用任何旧节点,它们将不会被销毁,而是会被孤立.它们属于文档但不再位于DOM树中.当您的代码访问它们时,不会发生任何事情或者它可能会引发错误.

  • 这两个问题都意味着它对js插件不友好 - 插件可能会附加处理程序或记住旧节点并导致内存泄漏.

  • 如果您养成使用innerHTML进行DOM操作的习惯,您可能会意外地更改属性或执行其他您不想要的操作.

  • 你拥有的节点越多,效率越低,电池就越多.

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


最好的选择是<input>,我没有看到其他答案提到:

Element.insertAdjacentHTML

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

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

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

不要再写elm.insertAdjacentHTML( 'beforeend', str )了.

  • +1 强调“.innerHTML”的问题。不过,我不太相信“Element.insertAdjacentHTML”作为替代方案。`.innerHTML` 有助于替换元素内的所有内容,但 `Element.insertAdjacentHTML` 是关于插入,而不是替换。 (2认同)

Kam*_*ski 8

短的

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

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

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

在此处输入图片说明

function up() {
  var container = document.createElement('div');
  container.id = 'container';
  container.innerHTML = "<p>Init <span>!!!</span></p>"
  document.body.appendChild(container);
}

function down() {
  container.remove()
}

up();

// innerHTML+=
container.innerHTML += "<p>Just first <span>text</span> here</p>";
container.innerHTML += "<p>Just second <span>text</span> here</p>";
container.innerHTML += "<p>Just third <span>text</span> here</p>";

down();up();

// innerHTML += str
var s='';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML += s;

down();up();

// innerHTML = innerHTML+str
var s=container.innerHTML+'';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML = s;

down();up();

// innerHTML = str
var s="<p>Init <span>!!!</span></p>";
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML = s;

down();up();

// insertAdjacentHTML str
var s='';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.insertAdjacentHTML("beforeend",s);

down();up();

// appendChild
var p1 = document.createElement("p");
var s1 = document.createElement("span"); 
s1.appendChild( document.createTextNode("text ") );
p1.appendChild( document.createTextNode("Just first ") );
p1.appendChild( s1 );
p1.appendChild( document.createTextNode(" here") );
container.appendChild(p1);

var p2 = document.createElement("p");
var s2 = document.createElement("span"); 
s2.appendChild( document.createTextNode("text ") );
p2.appendChild( document.createTextNode("Just second ") );
p2.appendChild( s2 );
p2.appendChild( document.createTextNode(" here") );
container.appendChild(p2);

var p3 = document.createElement("p");
var s3 = document.createElement("span"); 
s3.appendChild( document.createTextNode("text ") );
p3.appendChild( document.createTextNode("Just third ") );
p3.appendChild( s3 );
p3.appendChild( document.createTextNode(" here") );
container.appendChild(p3);

down();up();

// insertAdjacentHTML
container.insertAdjacentHTML("beforeend","<p>Just first <span>text</span> here</p>");
container.insertAdjacentHTML("beforeend","<p>Just second <span>text</span> here</p>");
container.insertAdjacentHTML("beforeend","<p>Just third <span>text</span> here</p>");

down();up();

// appendChild and innerHTML
var p1 = document.createElement('p');
p1.innerHTML = 'Just first <span>text</span> here';
var p2 = document.createElement('p');
p2.innerHTML = 'Just second <span>text</span> here';
var p3 = document.createElement('p');
p3.innerHTML = 'Just third <span>text</span> here';
container.appendChild(p1);
container.appendChild(p2);
container.appendChild(p3);
Run Code Online (Sandbox Code Playgroud)
b {color: red}
Run Code Online (Sandbox Code Playgroud)
<b>This snippet NOT test anythig - only presents code used in tests</b>
Run Code Online (Sandbox Code Playgroud)


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


Thi*_*iff 7

另一种选择是.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 );
Run Code Online (Sandbox Code Playgroud)

HTML

<div id="targetID">hello world</div>
Run Code Online (Sandbox Code Playgroud)