4

I have a curiosity with a plugin I have written for Knockout, called knockout-fast-foreach, namely what is the fastest way to repeatedly clone a set of nodes and inject them into the DOM.

There are two things that need to happen in the cloning, namely copying out the source template nodes, and injecting them back into the DOM.

Now there are some design choices that apply, that include:

  1. The source nodes will be children of a single DOM entity;
  2. The target may have siblings unaffected by the DOM injection i.e. not all the child nodes may change;
  3. The source may be a <template>, <script> or regular HTML DOM node.

So for example:

<template id='src'>ø</template>
<div id='target' data-bind='fastForEach: $data'>
</div>

When one applies the binding with ko.applyBindings([1, 2, 3], document.getElementById('target')) the result will be:

<template id='src'>ø <span data-bind='text: $data'></span></template>
<div id='target' data-bind='fastForEach: $data'>
  ø <span data-bind='text: $data'>1</span>
  ø <span data-bind='text: $data'>2</span>
  ø <span data-bind='text: $data'>3</span>
</div>

While that example is KO-specific, the performance of the DOM manipulation ought to be a relatively universal characteristic.

As you can see from the linked source code above, the way I have come up with so far is to copy the source nodes into an array, then clone + inject them into the target at the desired position.

Is is possible there is a faster way to clone and copy multiple elements (e.g. perhaps using document fragments)?

Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343
  • 3
    You have an interesting question here, but it is really rather broad. Perhaps you could try [CodeReview.StackExchange.com](http://CodeReview.StackExchange.com)? On a side note, the actual code is on a linked external site, but I'd say the relevant bits need to be in the question to prevent link rot c.q. make the question self-contained. I think the same would hold for CodeReview SE, where links to full code bases are appreciated, but the question's expected to have the relevant code too. – Jeroen Mar 09 '15 at 14:12
  • Do you need to worry about "copying" events and properties when cloning the elements? Or is just copying attributes sufficient? If it is the latter, as element count goes up, it is almost *always* going to be more efficient to work with html strings (via getting and setting `innerHTML`) instead of working with actual DOM elements. – Brandon Mar 09 '15 at 17:25
  • Thanks @Brandon; just the structure + attributes; there are no events and properties to worry about. As you may note though from restriction #2, one could not efficiently use `innerHTML` since not all children of the given node may be changing. – Brian M. Hunt Mar 09 '15 at 18:10
  • Seems like someone took the [time to test](http://jsperf.com/fragment-vs-appendchild-vs-innerhtml/6). @Brandon What is the source for your statement? In all tests I've seen/ done and all articles I've read, it is always said that (logically) DOM methods are faster than strings because they do not cause a DOM repaint (see [here](https://gist.github.com/oliverfernandez/5619180), [here](http://stackoverflow.com/questions/2305654/innerhtml-vs-appendchildtxtnode), [here](http://blog.mikie.iki.fi/2014/05/innerhtml-vs-appendnode-vs.html) and more. – webketje Mar 10 '15 at 01:28
  • @Tyblitz the version of the test you are looking at is no good. The innerHtml version is performing needless DOM manipulation which makes it appear slow. Look at this version that fixes that; http://jsperf.com/fragment-vs-appendchild-vs-innerhtml/18 For large Dom updates building HTML strings usually outperforms everything else. It has nothing to do with repaints and everything to do with context switches. At any rate it cannot help the OP due to his stated constraints. – Brandon Mar 10 '15 at 01:39
  • 1
    @Brandon Last note on this: I still feel sceptical about it; I can't believe the 'general opinion' amongst developers is so 'wrong'; or at least that it's so black and white (+ the test doesn't account for actual 'painting' with CSS styles set). I started this 'issue' at the [Github repo](https://github.com/knockout/knockout/issues/1669) a while ago, feel free to share your thoughts there :). – webketje Mar 10 '15 at 02:06
  • 1
    One obvious optimization in your cloning procedure is to eliminate the double-cloning of elements when your source is `SCRIPT`. Change that if condition to `else if (sourceNode.tagName === 'SCRIPT') { container.innerHTML = sourceNode.text; return container; }`. You are also doing an extra (unnecessary?) clone on [line 67](https://github.com/brianmhunt/knockout-fast-foreach/blob/master/index.js#L67) – Brandon Mar 10 '15 at 12:26

1 Answers1

1

You are using data-binding. This is in itself going to be slow. The best performance is always going to be to manipulate a string off the dom and then insert it into the dom in one go - element.innerHTML = "your new html". Even better to have it not positioned inline because this slows the rendering of the browser. Itereratively adding to the dom is thrashing the browser renderer. See - http://davidwalsh.name/css-js-animation.

Steve Tomlin
  • 3,391
  • 3
  • 31
  • 63