why is that using a document fragment can improve performance? - javascript

See the codes below. Insert nodes into the page.
var el;
var i = 0;
var body = document.querySelector('body');
var fragment = document.createDocumentFragment();
while (i < 10000) {
el = document.createElement('p');
el.innerText = 'This is item number ' + i;
fragment.appendChild(el);
i++;
}
body.appendChild(fragment);
vs
var el;
var i = 0;
var body = document.querySelector('body');
while (i < 10000) {
el = document.createElement('p');
el.innerText = 'This is item number ' + i;
body.appendChild(el);
i++;
}
As I know, JS excution would block the rendering process. The rendering process should begin right after the JS excution. I think all the newly inserted nodes were rendered at once even though the container appendChilds each node.
How does using a fragment avoid reflows and improve performance?
It's not a appendChild-render-appendChild-render process.

Related

JavaScript createElement Line Causes Error

I'm writing a simple JavaScript program to examine every element in an HTML website and add a child node to every non-text node that labels the type of tag:
Here is my code:
window.onload = function() {
var body_elems = document.body.getElementsByTagName("*");
for (var i = 0; i < body_elems.length; i++) {
if (body_elems[i].nodeType != 3) {
var tag_name = body_elems[i].tagName;
var child = document.createElement("P");
var child_text = document.createTextNode(tag_name);
child.appendChild(child_text);
body_elems[i].appendChild(child);
body_elems[i].firstChild.className = "hoverNode";
}
}
}
For some reason the line var child = document.createElement("P") causes the page to never load; if I comment out that line then the page will load.
On the other hand, here is a slightly different version that actually works:
window.onload = function() {
var body_elems = document.body.getElementsByTagName("*");
for (var i = 0; i < body_elems.length; i++) {
if (body_elems[i].nodeType != 3) {
var tag_name = body_elems[i].tagName;
var child = document.createTextNode(tag_name);
body_elems[i].appendChild(child);
child.className="hoverNode";
}
}
}
But I can't figure out how to assign a class name to the new node in order to apply CSS, so if someone could tell me how to do that my problem would be solved.
I'm new to JS so any help would be much appreciated.

Looping through a set of <p>'s one at a time

I'm trying to figure out how to count the number of p's so every time the button is pressed, it outputs to 0 to 1 until the maximum number of p's is counted.
var big_number = 999999;
var i;
var a = document.getElementsByTagName("p");
function function0() {
for (i=0; i < big_number; i++) {
document.getElementsByTagName("p")[i].innerHTML="text";
}
}
I want it to write to another p every time the button is pressed.
document.getElementsByTagName("p").length // number of p elements on the page
Is that what you were asking?
Make a generic tag adder function then call it:
function addTags(tagName,start, max, container) {
var i = start;
for (i; i < max; i++) {
var newp = document.createElement(tagName);
newp.innerHTML = "paragraph" + i;
container.appendChild(newp);
}
}
var tag = 'p';
var big_number = 30;
var i;
var a = document.getElementsByTagName(tag );
// **THIS is your specific question answer**:
var pCount = a.length;
var parent = document.getElementById('mydiv');
addTags(tag,pCount , big_number, parent);
// add 10 more
a = document.getElementsByTagName(tag );
pCount = a.length;
big_number = big_number+10;
addTags(tag,pCount , big_number, parent);
EDIT:
NOTE: THIS might be better, only hitting the DOM once, up to you to determine need:
function addTagGroup(tagName, start, max, container) {
var tempContainer = document.createDocumentFragment();
var i = start;
for (i; i < max; i++) {
var el = document.createElement(tagName);
el.textContent = "Paragraph" + i;
tempContainer.appendChild(el);
}
container.appendChild(tempContainer);
}
To find out how many <p> elements there are in the document you should use DOM's length property as below :-
var numP = document.getElementsByTagName("P").length;
or
var div = document.getElementById("myDIV");
var numP = div.getElementsByTagName("P").length;
To get number of element inside a tag.

How to appendChild(element) many times. (The same element)

My question is:
Is that possible to add the same element without rewriting the same variable.
I am creating a slider, and i need to append a div with a class slide-el into block slider.
Here is a part of code
var body, html, sliderBody, btnLeft, btnRight, i, parts, vHeight, vWidth;
//Variable definitions
var i = 0,
parts = 3,
//Main html elements
body = document.body,
html = document.element,
//viewport Height and Width
vHeight = window.innerHeight,
vWidth = window.innerWidth,
sliderBody = _id("slider"),
btnLeft = _id("btn-left"),
btnRight = _id("btn-right"),
urls = ["http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg"];
slide = _createEl("div");
slide.className += "slide-el";
function _id(el){
return document.getElementById(""+ el +"");
}
function _createEl(el){
return document.createElement(""+ el +"");
}
window.onload = function(){
slideLayout();
}
function slideLayout(){
for(var i=0; i < urls.length; i++){
sliderBody.appendChild(slide);
}
}
The problem is that I can't append the same element that many times. It just creates one element instead of 4.
For you to understand better I made a fiddle:
https://jsfiddle.net/ud7dvn3z/
appendChild will remove the node from wherever it is before appending it to its new location, so you need to make copies of the node instead. You can use cloneNode for that. The true makes cloneNode perform a deep clone, i.e. with all its child nodes.
for(var i = 0; i < urls.length; i++){
sliderBody.appendChild(slide.cloneNode(true));
}
Okey guys! I found an answer. I have to put
slide = _createEl("div");
slide.className += "slide-el";
into for loop.
Now it looks like this:
for(var i=0; i < urls.length; i++){
slide = _createEl("div");
slide.className += "slide-el";
sliderBody.appendChild(slide);
}

Why does the memory usage not grow when I create 10,000 elements?

The memory usage doesn't grow when I create 10,000 elements. But when I make references to these 10,000 elements, the memory usage grows from 3.5M to 4.0M. And the usage decreases by 0.1M when I destroy the reference while deleting the elements makes it decrease by 0.4M.
Here are my questions:
Why doesn't memory usage grow when I create 10,000 elements?
Why does memory usage grow significantly when I make references to these 10,000 elements?
Why the usage decreases only slightly when references are destroyed while deleting the elements decreases it obviously?
OS: El Capitan 10.11.3
Browser: Chrome 48.0.2564.116 (64-bit)
After creating elements (3.5M memory usage)
After making references (4.0M memory usage)
(function(){
var elemArray = [];
var elemCount = 10000;
//create 10000 elements and append to the dom tree
var create = function(){
var i = 0;
var zone = document.getElementById("zone");
for(;i<=elemCount;i++){
var div = document.createElement("div");
div.id = "div" + i;
div.innerHTML = "the " + i + " div";
zone.appendChild(div);
}
};
document.getElementById("create").addEventListener("click",create,false);
var clear = function(){
var zone = document.getElementById("zone");
zone.innerHTML = "";
};
document.getElementById("clear").addEventListener("click",clear,false);
var link = function(){
var i = 0;
for(;i<=elemCount;i++){
elemArray[i] = document.getElementById("div" + i);
}
};
document.getElementById("link").addEventListener("click",link,false);
var unlink = function(){
if(elemArray.length > 0)
elemArray.splice(0,elemArray.length);
}
document.getElementById("unlink").addEventListener("click",unlink,false);
})();
<button id="create" >create 10000 elements</button>
<button id="clear" >delete 10000 elements</button>
<button id="link" >reference 10000 elements</button>
<button id="unlink" >destroy reference</button>
<div id="zone"></div>
Everything appears to be working as expected.
OP's code adds elements to the DOM which uses the C++ memory heap. Then when Javascript attaches to those elements a wrapper object is created which uses Javascript memory. And that memory usage then shows up in the Chrome memory profiler.
Interestingly, if you add a name attribute to each new div then there is an immediate 0.5mb spike of memory use. Just adding an id alone (like OP's code) does not produce that spike (with a div). This can be tested using the code snippet below and the Chrome profiler.
Here's a previous SO question that might explain it better:
Do DOM tree elements with ids become global variables?
Test Code
var LIMIT = 10000,
zone = document.getElementById('zone'),
count = document.getElementById('count');
window.b1.onclick = function() {
var i;
for (i = 0; i < LIMIT; i++) {
zone.appendChild(document.createElement('div'));
}
show();
}
window.b2.onclick = function() {
var i, e;
for (i = 0; i < LIMIT; i++) {
e = document.createElement('div');
e.id = 'id' + i;
zone.appendChild(e);
}
show();
}
window.b3.onclick = function() {
var i, e;
for (i = 0; i < LIMIT; i++) {
e = document.createElement('div');
e.name = 'na' + i;
zone.appendChild(e);
}
show();
}
window.b4.onclick = function() {
var i, e;
for (i = 0; i < LIMIT; i++) {
e = document.createElement('div');
e.id = 'id' + i;
e.name = 'na' + i;
zone.appendChild(e);
}
show();
}
window.b5.onclick = function() {
zone.innerHTML = '';
show();
}
function show( ) {
var e = zone.getElementsByTagName('div');
count.innerHTML = 'total elements = ' + (e ? e.length: '0');
}
button {
width: 8em;
}
<div>Memory Test: <span id="count"></span></div>
<button id="b1">none</button>
<button id="b2">with id</button>
<button id="b3">with name</button>
<button id="b4">with name + id</button>
<button id="b5">clear</button>
<div id="zone"></div>

Append an Array to an Unordered List

What I'm trying to accomplish with this code is to output the array alphabet as a series of list items into an existing unordered list in the actual markup. I've got the array into list items, but I can't figure out how to tell it to append itself to an existing unordered list <ul id="itemList"></ul>.
var itemsExist = true;
var indexNum = 0;
var unorderedList = document.getElementById('itemList');
var alphabet= new Array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
function write_letters(){
for (i = 0; i < alphabet.length; i++ ) {
document.write('<li>' + alphabet[indexNum++] + '</li>');
}
}
if (itemsExist){
write_letters();
} else {
document.write("error!");
}
Don't use document.write to do it. You should act like this:
function write_letters(){
var letters = "";
for (var i = 0; i < alphabet.length; i++ ) {
//Also I don't understand the purpose of the indexNum variable.
//letters += "<li>" + alphabet[indexNum++] + "</li>";
letters += "<li>" + alphabet[i] + "</li>";
}
document.getElementById("itemList").innerHTML = letters;
}
More proper way is to use DOM (in case you want full control of what's coming on):
function write_letters(){
var items = document.getElementById("itemList");
for (var i = 0; i < alphabet.length; i++ ) {
var item = document.createElement("li");
item.innerHTML = alphabet[i];
items.appendChild(item);
}
}
You can use a combination of createElement() and appendChild() to add new HTML elements within another HTML element. The code below should work for you:
<html>
<head>
<title>Script Test</title>
</head>
<body>
<ul id="itemList"></ul>
</body>
<script>
var itemsExist = true;
var indexNum = 0;
var unorderedList = document.getElementById('itemList');
var alphabet= new Array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
var myElement;
function write_letters(){
for (i = 0; i < alphabet.length; i++ ) {
// Create the <LI> element
myElement = document.createElement("LI");
// Add the letter between the <LI> tags
myElement.innerHTML = alphabet[indexNum++];
// Append the <LI> to the bottom of the <UL> element
unorderedList.appendChild(myElement);
}
}
if (itemsExist){
write_letters();
} else {
document.write("error!");
}
</script>
</html>
Note how the script exists below the body tag. This is important if you want your script to work the way you wrote it. Otherwise document.getElementById('itemList') will not find the 'itemList' ID.
Try to reduce the actions on the DOM as much as possible. Every appendChild on unorderedList forces the browser to re-render the complete page. Use documentFragement for that sort of action.
var frag = document.createDocumentFragment();
for (var i = alphabet.length; i--; ) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(alphabet[indexNum++]));
frag.appendChild(li);
}
unorderedList.appendChild(frag);
So there will be only one DOM action which forces a complete redraw instead of alphabet.length redraws

Categories

Resources