Fastest way to repeatedly clone a set of nodes - javascript

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:
The source nodes will be children of a single DOM entity;
The target may have siblings unaffected by the DOM injection i.e. not all the child nodes may change;
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)?

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.

Related

how to reference a child element on a cloned element

I am running some tests on a DOM element,
the result of the tests is one of the element descendants.
for example:
<div id="myelement" class="some-class">
<div class="some-child-class"></div>
<div class="some-other-child-class">
<div class="grandchild-class"></div>
<div class="another-grandchild"></div>*
</div>
</div>
let's assume that:
test(document.getElementById('myelement'));
will return the Element marked with asterisk
Now my problem is:
The test procedure is heavy and resource consuming.
I don't want to run it when i don't have to.
And sometimes, I clone an element that has already been tested (meaning - i KNOW the result of the test), but since I am getting an object reference as a result I can't use it to access the relevant child on the cloned element.
Is there any efficient way of somehow "save" the relative path from a parent Element to a specific descendant DOM element and then "apply" it on another element?
So you could assign unique ids to each element and cache the test results in an Object at the Elements id. However, i dont know if this is useful. An example implementation:
var test=function(el){
return this[el.id] || (this[el.id]=advancedtesting(el));
}.bind({});
So you could do:
test(document.getElementById('myelement'));//caches
test(document.getElementById('myelement'));//returns the cached
You could use jQuery for that.
The selectors they use can take the form of 'nth-child' or 'nth-of-type' (see documentation here).
What is does is that targets child element from the position they have relative from where you start from.
In your case if you want to start from your first element and go down to the starred one you can do:
$('#myelement').find('div:nth-child(2) > div:nth-child(2)')
What this does is that it takes #myelement as a base from which you will begin the search, and after that it goes down to the second child element that is a div, and again into this div's second child element.
You could reuse that selector with a different base.

jquery selector executed on DOM sub-tree (as in backbone)

Be default, jQuery's object $ allows you to run selectors over entire DOM tree. However, Backbone (which relies on jQuery) allows you to run the selector not on entire (global) DOM, $, but also on local backbone view (this.$ in backbone views). It's just faster, since we don't traverse entire DOM tree, but just its part.
The question is: how to achieve that in pure jQuery (no backbone)? A code example would be appreciated.
You use find:
$(someElement).find("selector").doSomething();
There's also a likely-to-be-deprecated-at-some-stage form you'll sometimes see people using that looks like this:
$("selector", someElement).doSomething();
...but literally all jQuery does with that is turn around and call find.
Example looking within a div for a span with a given class:
// Get the div
var div = $("#the-div");
// Find the span within it, turn it green
div.find(".foo").css("color", "green");
<div id="the-div">
<span>Not this one.</span>
<span class="foo">This one.</span>
</div>
<div>
<span>Not this one.</span>
<span class="foo">Not this one either, despite it having the class; it's in the wrong div.</span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

CSS DOM Traversal superior than JavaScript DOM Traversal?

JavaScript and CSS both use its own DOM tree when traversing through HTML elements.
In JavaScript, we can use its own DOM traversal methods such as
element.parentNode
element.nextSibling
However, this way is very unintuitive because JavaScript's DOM Tree contains things other than the HTML elements which can be easy to mess up for a developer.
i.e.
<div>
<p>
</p>
</div>
JavaScript's DOM Tree has <div> as the parent and 3 children:
text node: \n\t
element node: <p>
text node: \n
This becomes increasingly more difficult to keep track as the HTML document structure increases in complexity because not all HTML elements have a newline before and after among other things.
Thankfully, JavaScript allows us to use CSS's DOM traversal using:
element.querySelector("CSSselectorHere")
Since CSS's DOM Tree only contains the HTML elements, that makes it much easier to implement.
The case only I could think of where JavaScript's DOM Tree would be advantageous is if you are trying to color the text "cdf" red in the following:
In HTML:
<p> abc link cdf </p>
In JavaScript:
pElementVariable.firstChild.nextSibling.nextSibling.classList.add("CSSclassRed");
However, wouldn't a better HTML practice be to just enclose the unique text like so?
<p> abc link <span>cdf</span> </p>
Thus, styling the span using CSS's DOM traversal is possible. (assuming we're using traversal methods only, no ids)
If so, does .querySelector, an enabler in JavaScript for CSS's DOM traversal, make JavaScript's own built-in DOM traversal methods obsolete?
No. CSS is more limited, because it can only select elements (and pseudo-elements, but not through querySelector, but that might change).
Using the DOM you can traverse arbitrary nodes in the tree. That's more powerful. And if you want to restrict to elements, you can:
node.childNodes; // All child nodes
parentNode.children; // Only element child nodes
node.firstChild; // First node child
parentNode.firstElementChild; // First element child
node.lastChild; // Last node child
parentNode.lastElementChild; // Last element child
node.previousSibling; // The previous sibling node
nonDoctypeChildNode.previousElementSibling; // The previous sibling element
node.nextSibling; // The next sibling node
nonDoctypeChildNode.nextElementSibling; // The next sibling element
node.parentNode; // The parent node
node.parentElement; // The parent node, only if it's an element
So you don't need CSS APIs for things like that. And in particular you shouldn't modify your HTML to be able to use CSS APIs.

HTML Template Element: Clarification

I have recently discovered the HTML template element, and would appreciate some clarification.
As far as I can tell, the three distinguishing features are:
template is not rendered on screen
template does not make its way to the DOM
there is an additional content property which acts as a virtual element between the template and its contents
Aside from these, a <template> might have been a div, hidden from the screen and the DOM. In this regard, the JavaScript used to take advantage of the template is the same as if trying to fake one using a div.
Is this correct? I ask because it would be relatively easy to fake one for nonsupporting browsers.
There are some major differences:
Script (<script>) in a <template> element is not executed immediately.
Media content (<audio>, <video>, <img>) inside the template won't be loaded immedialtey.
Instead they will be executed or loaded once they are inserted in the rendered DOM tree.
querySelector() method calls and CSS selectors won't explore the content of a <template>, but you can still access the elements inside by using querySelector() on its content property.
With HTML5 template, you can access all its content in the form of a Document Fragment, and insert it in the DOM tree using appendChild(), instead of modifying innerHTML.
<template> elements can be placed in the <head> element.
You access the template content as a string using .innerHTML or as a DocumentFragment using .content.
It's quite difficult then to fake all thses behaviors. There are some polyfills that can partially do that. I'd recommend Neovov's one.
Look at this illustrative example:
//explore the content of the template
var script = T1.content.querySelector( 'script' )
console.log( script.innerHTML )
function insert() {
//insert the real templete
Target1.appendChild( T1.content.cloneNode( true ) )
//insert the fake template
Target2.innerHTML += D1.innerHTML
}
<template id=T1>
Real Template -
<script>console.log( 'executed at each insertion' )</script>
</template>
<div hidden id=D1>
Fake Template -
<script>console.log( 'executed only at parsing' )</script>
</div>
<button onclick="insert()">insert</button>
<div id=Target1><div>
<div id=Target2><div>
In short, the template tag is exactly what it sounds like.
It is just a template for the JavaScript to create in the future.
It is slightly different from a hidden <div> though, but basically you are right and it can be faked with a hidden div
Imagine this scenario where you have a webpage with 2 inputs, one that enters your name and one that enters your age. By using a template tag to create a simple table, you can then use JavaScript to populate the table with your inputs, in the same page.
This article has a good read on it:
https://www.sitepoint.com/html5-template-tag/

Accessing all inner elements in Polymer?

I have:
<k-myelement>
<k-anotherelement></k-anotherelement>
</k-myelement>
When I define the template like this:
<polymer-element name="k-myelement">
<template>
<content select="k-anotherelement" id="anotherelement"></content>
</template>
</polymere-element>
I can access the inner element with this.$['anotherelement']
But with this approach I have to predefine, which inner elements can be used.
What I want is a template technique, that allows me to access all the inner elements.
<content> (insertion points) are for rendering elements in the light DOM at specific locations in the Shadow DOM. Using <content select="k-anotherelement"></content> says "render any <k-anotherelement> elements here. If you want all light DOM nodes to be invited into the rendering party, simply use <content></<content>.
The other issues with your snippet:
The name of the element needs to be defined on <polymer-element>, not as <template name="k-myelement">
To get the list of nodes that pass through a <content>, use content.getDistributedNodes(). You may also want to consider if you even need <content>. Light DOM children nodes can be access with .children and the other accessors. From the Polymer docs:
For a <content>, you can iterate through content.getDistributedNodes() to get the list of nodes distributed at the insertion point.
In Polymer, the best place to call this method is in the attached() callback so you’re guaranteed that the element is in the DOM tree.
Also remember that you can access the light DOM as the element’s normal children (i.e. this.children, or other accessors). The difference with this approach is that it’s the entire set of potentially distributed nodes; not those actually distributed.

Categories