Movatterモバイル変換


[0]ホーム

URL:


MozillaHacks

insertAdjacentHTML() Enables Faster HTML Snippet Injection

ByJanet Swisher

Posted on November 9, 2011inFirefox

The following is a guest post byHenri Sivonen:

In Firefox 8, we’ve added support forinsertAdjacentHTML(). It’s an ancient feature of Internet Explorer that has recently been formalized in HTML5 and then spun out into theDOM Parsing specification. The bad news is that Firefox is the last major browser to implement this feature. The good news is that since other major browsers implement it already, you can start using it unconditionally as soon as the Firefox 8 update has been rolled out to users.

Basic Usage

insertAdjacentHTML(<var>position</var>, <var>markup</var>) is a method onHTMLElement DOM nodes. It takes two string arguments. The first argument is one of"beforebegin","afterbegin","beforeend", or"afterend" and gives the insertion point relative to the node thatinsertAdjacentHTML() is invoked on. The second argument is a string containing HTML markup that gets parsed as an HTML fragment (similar to a string assigned toinnerHTML) and inserted to the position given by the first argument.

If the node thatinsertAdjacentHTML() is invoked on is ap element
with the text content “foo”, the insertion points would be where the comments are in the following snippet:

foo

The"beforebegin" and"afterend" positions work only if
the node is in a tree and has an element parent.

For example, consider the following code:

foo

This code produces this log output:

foobar

Well, that does not look particularly special. In fact, it looks like something that could have been done using plain oldinnerHTML. So why bother withinsertAdjacentHTML() when<var>element</var>.innerHTML += "<var>markup</var>"; already works?

There are two reasons.

Avoiding DOM corruption

Let’s consider the DOM corruption issue first. When you do<var>element</var>.innerHTML += "<var>markup</var>";, the browser does the following:

  1. It gets the value ofinnerHTML by serializing the descendants of<var>element</var>.
  2. It appends the right hand side of+= to the string.
  3. It removes the children of<var>element</var>.
  4. It parses the new string that contains the serialization of the old descendants followed by some new markup.

The old descendants might have been script-inserted to form a subtree that doesn’t round-trip when serialized as HTML and reparsed. In that case, after the operation, the tree would have a different shape even for the “old” parts. (For example, if<var>element</var> had ap child which in turn had adiv child, the subtree wouldn’t round-trip.) Furthermore, even if serializing and reparsing resulted in a same-looking tree, the nodes created by the parser would be different nodes than the nodes that were children of<var>element</var> at first. Thus, if other parts of the JavaScript program were holding references to descendants of<var>element</var>, after<var>element</var>.innerHTML += "<var>markup</var>"; had been executed, those references would point to detached nodes and<var>element</var> would have new similar but different descendants.

When additional content is inserted usinginsertAdjacentHTML(), the existing nodes stay in place.

Better performance

Serializing and reparsing is also what leads to performance problems with the<var>element</var>.innerHTML += "<var>markup</var>"; pattern. Each time some more content is appended, all the existing content in<var>element</var> gets serialized and reparsed. This means that appending gets slower and slower, because each time there more and more previous content to serialize and reparse.

UsinginsertAdjacentHTML() can make a big difference. For testing purposes, I started with an emptydiv and ran a loop that tried to append as many tweets as possible to thediv in five seconds. A tweet is actually rather large when you count all the mark-up that implements @mention linkification, the name of the tweeter, retweet and favoriting UI, etc. It weighs about 1600 characters of HTML source—most of it mark-up.

On the computer that I used for testing, theinnerHTML way of appending managed to append onlyslightly over 200 tweets in five full seconds. In contrast, theinsertAdjacentHTML("beforeend", ...) way of appending managed to appendalmost 30,000 tweets in 5 seconds. (Yes, that’s hundreds versus tens of thousands.) Obviously, real Web apps should never block the event loop for five seconds—this is just for benchmark purposes. However, this illustrates how theinnerHTML way of appending becomes notably slower as more and more content accumulates to be serialized and reparsed each time.

At this point, some readers might wonder ifinsertAdjacentHTML() offers any benefit overcreateContextualFragment(). After all, conceptuallyinsertAdjacentHTML() creates a fragment and inserts it.

UsingcreateContextualFragment(), my test manages only slightly over 25,000 tweets in five seconds, while usinginsertAdjacentHTML() manages slightly under 30,000. This is because Gecko acceleratesinsertAdjacentHTML() when the insertion point has no next sibling (only for HTML though—not for XML so far). The"beforeend" insertion point never has a next sibling and is always accelerated (for HTML). The"beforebegin" insertion point always has a next sibling (the node thatinsertAdjacentHTML() was invoked on) and is never accelerated. For"afterbegin" and"afterend", whether the operation is accelerated depends on the situation.

In conclusion, you can make your Web app perform better by using<var>element</var>.insertAdjacentHTML("beforeend", "<var>markup</var>"); where you currently use<var>element</var>.innerHTML += "<var>markup</var>";.

About Janet Swisher

Janet is the Community Lead and Project Manager for MDN Web Docs. She joined Mozilla in 2010, and has been involved in open source software since 2004 and in technical communication since the 20th century. She lives in Austin, Texas, with her husband and a standard poodle.

More articles by Janet Swisher…

Discover great resources for web development

Sign up for the Mozilla Developer Newsletter:

Thanks! Please check your inbox to confirm your subscription.

If you haven’t previously confirmed a subscription to a Mozilla-related newsletter you may have to do so. Please check your inbox or your spam filter for an email from us.


15 comments

  1. Alex Chang

    Awesome~! Finally, it is about time.

    November 9th, 2011 at 21:32

  2. Fatih

    Nice to hear some performance tips about HTML snippet injection.

    November 9th, 2011 at 22:00

  3. Maurizio DOmba

    Thank you for this article… It’s a long time I did not read an article so interesting… insertAdjacentHTML() is a great tip!

    November 10th, 2011 at 00:21

  4. Rene Kriest

    Very interesting read with impressive benchmarks.

    So maybe in a couple of years we can abandon .innerHTML() ;)

    November 10th, 2011 at 01:16

  5. Theodor

    Thanks for implementing and explaining the performance issues!

    November 10th, 2011 at 01:58

  6. Ruturaj

    great,
    nice read on why its better than innerHTML !
    Hope this HTML5 standard gets into all browsers

    November 10th, 2011 at 04:10

  7. pd

    Presumably libraries like jQuery will now get faster and smaller by using this for their .prepend(), .append() and similar methods. Can anyone confirm this?

    November 10th, 2011 at 05:17

  8. timmywil

    Could you provide your perf tests for createContextualFragment vs insertAdjacentHtml? If so, I would like to use the same tests in other browsers to measure their performance.

    November 10th, 2011 at 07:33

  9. Henri Sivonen

    @timmywil, the tests I used are athttp://hsivonen.iki.fi/test/tweets/

    November 11th, 2011 at 03:42

  10. Richard Kimber

    Yay for innovative IE features!

    November 11th, 2011 at 06:01

  11. Manuel Strehl

    Finally I realize what this new method is for. Thank you for the overview!

    Can you estimate, how much less performant a createDocumentFragment() + frag.innerHTML followed by appendChild (or insertBefore) would be compared to insertAdjacentHTML?

    November 11th, 2011 at 06:08

  12. Jamie Murphy

    finally! thanks for the well written post too! :)

    November 11th, 2011 at 06:59

  13. Dmitry Pashkevich

    I have a piece of logic in my webapp where the inner contents of an element is completely re-rendered. Can you tell if using insertAdjacentHtml will give a performance benefit over innerHTML assignment?

    So that is something like

    el.innerHTML=’New content’
    vs
    el.innerHTML=”;
    el.insertAdjacentHTML(‘afterbegin’, ‘New content’);

    Is insertAdjacentHTML still better to use if I’m completely re-rendering an element or assigning inner html to it after creation?

    November 11th, 2011 at 07:23

  14. Henri Sivonen

    @Dmitry Pashkevich:

    If you are removing the previous children of the element anyway, it is not better to use insertAdjacentHTML than innerHTML.

    el.innerHTML=”;
    el.insertAdjacentHTML(‘afterbegin’, ‘New content’);

    is slightly less efficient than

    el.innerHTML=’New content’;

    It makes sense to use insertAdjacentHTML when you want to keep the existing children of the node and add some more.

    November 14th, 2011 at 02:20

  15. Marcos Zanona

    About time huh!! really nice :)

    November 16th, 2011 at 08:38

Comments are closed for this article.


[8]ページ先頭

©2009-2025 Movatter.jp