HashedPassword
HashedPassword

Reputation: 169

Slow creation of divs using innerHTML and +=

I am currently retrieving a lot of information from a database and would like to display this info as miniatures on a site. Every row gathered from the database contains an image and some other info that appears within the miniature.

When the information is gathered I use a function called printAllMini, which prints all of the divs into the container using printMini for each row of information.

printAllMini = function() {
    console.log("PRINTING!");
    document.getElementById("mini_blocks").innerHTML = "";
    for (var i = 0; i < docs.length; i++) {
	var object = docs[i];
	printMini(object.id, object.arrived, object.booked, object.verification, object.org, object.price, object.stax, object.pic);
	console.log(i);
    }
    console.log("DONE!");
}

printMini = function(id, arrived, booked, verification, org, price, stax, pic) {
    if (verification != "-") {
	// So that no string is empty
	if (id == "") { id = "-"; }
	if (arrived == "") { arrived = "-"; }
	if (booked == "") { booked = "-"; }
	if (verification == "") { verification = "-"; }
	if (org == "") { org = "-"; }
	if (price == "") { price = "-"; }
	if (stax == "") { stax = "-"; }
	document.getElementById("mini_blocks").innerHTML += '<div class="mini" id="\'' + id + '\'" onclick="changeInfo(\'' + arrived + '\', \'' + booked + '\', \'' + verification + '\', \'' + org + '\', \'' + price + '\', \'' + stax + '\', \'' + id + '\'), getPDF2(\'' + id + '\')"> <div class="mini_header"> <table class="mini_table"> <tr> <td align="left"> <b>Inkommen</b> </td> <td align="right"> <b>ID</b> </td> </tr> <tr> <td align="left">' + arrived + '</td> <td align="right">' + id + '</td> </tr> </table> </div> <div class="mini_img"> <img src="data:image/jpeg;base64,' + pic + '"> </div> <div class="mini_footer"> <table class="mini_table"> <tr> <td align="left"> <b>Bokförd</b> </td> <td align="right"> <b>Verifikat</b> </td> </tr> <tr> <td align="left">' + booked + '</td> <td align="right">' + verification + '</td> </tr> </table> </div> </div>';
    }
    else {
	document.getElementById("mini_blocks").innerHTML += '<div class="mini" id="\'' + id + '\'" onclick="changeInfo(\'' + arrived + '\', \'' + booked + '\', \'' + verification + '\', \'' + org + '\', \'' + price + '\', \'' + stax + '\', \'' + id + '\'), getPDF2(\'' + id + '\')"> <div class="mini_header2"> <table class="mini_table"> <tr> <td align="left"> <b>Inkommen</b> </td> <td align="right"> <b>ID</b> </td> </tr> <tr> <td align="left">' + arrived + '</td> <td align="right">' + id + '</td> </tr> </table> </div> <div class="mini_img"> <img src="data:image/jpeg;base64,' + pic + '"> </div> <div class="mini_footer2"> </div> </div>';
    }
}

Problem:

This is however really slow and takes upwards of 45 seconds to complete. There seems like it has to do with the line:

document.getElementById("mini_blocks").innerHTML += '<div'>;

...as it takes longer and longer for the divs to be created. The first 10 divs are created within the first second, but towards the end there is about one div created per second instead. It just gets slower and slower.

This gif shows the first couple of seconds of the process and when each div is done: GIF

I've tried (NOTICE THE + SIGN IN FRONT OF =) document.getElementById("mini_blocks").innerHTML += '<div>'

...which is a lot faster, it does the same thing but replaces all the previous div, so that I only end up with one div. This is almost instantaneous!

I also noticed that none of the divs shows up until all of the divs are done and I have no idea why.

Actual questions:

  1. Is there something that makes the += method slow when creating divs?
  2. Is there a reason why none of the divs shows up until the very end?
  3. I'm new to web-programming. Is there better ways to do the same thing?

Upvotes: 3

Views: 632

Answers (4)

Teemu
Teemu

Reputation: 23416

1) Yes there is. The DOM is recalculated every time you re-create an element. Also concatenating long strings might take time.

2) JavaScript is executed first, after that the page is rendered. Browser has only one thread for these tasks.

3) Yes, there are better ways. Create elements and append or insert them. This is usually more code, but faster results.

If it's hard to get rid of the strings to create HTML, use insertAdjacenHTML instead of innerHTML. It's faster because it doesn't re-create elements. Also if possible, create a HTMLString as big parts as possible, and put only one string to the DOM, that reduces the re-calculation time significantly.

Upvotes: 1

JoshBerke
JoshBerke

Reputation: 67108

1: In javascript string are immutable. So every time you modify the string you are actually creating a copy of the string, and appending your changes to it. As the string grows this can take a lot of time.

2: The browser won't render the HTML until your javascript has finished executing. You could process several of the divs then yield control and use a timeout to continue processing.

3: Don't create HTML using a massive string. I'd recommend, using a template driven approach. You might want to look into Reactjs or Angular. Otherwise use the dom methods to create your nodes and append them into the dom.

Upvotes: 1

freefaller
freefaller

Reputation: 19963

Instead of looking up document.getElementById("mini_blocks") and then setting .innerHTML on each iteration of the loop - try storing the value in a global variable (such I have here with printMiniResult) and set the .innerHTML just the once...

var printMiniResult = "";

printAllMini = function() {
    console.log("PRINTING!");
    for (var i = 0; i < docs.length; i++) {
	var object = docs[i];
	printMini(object.id, object.arrived, object.booked, object.verification, object.org, object.price, object.stax, object.pic);
	console.log(i);
    }
    document.getElementById("mini_blocks").innerHTML = printMiniResult;
    console.log("DONE!");
}

printMini = function(id, arrived, booked, verification, org, price, stax, pic) {
    if (verification != "-") {
	// So that no string is empty
	if (id == "") { id = "-"; }
	if (arrived == "") { arrived = "-"; }
	if (booked == "") { booked = "-"; }
	if (verification == "") { verification = "-"; }
	if (org == "") { org = "-"; }
	if (price == "") { price = "-"; }
	if (stax == "") { stax = "-"; }
	printMiniResult += '<div class="mini" id="\'' + id + '\'" onclick="changeInfo(\'' + arrived + '\', \'' + booked + '\', \'' + verification + '\', \'' + org + '\', \'' + price + '\', \'' + stax + '\', \'' + id + '\'), getPDF2(\'' + id + '\')"> <div class="mini_header"> <table class="mini_table"> <tr> <td align="left"> <b>Inkommen</b> </td> <td align="right"> <b>ID</b> </td> </tr> <tr> <td align="left">' + arrived + '</td> <td align="right">' + id + '</td> </tr> </table> </div> <div class="mini_img"> <img src="data:image/jpeg;base64,' + pic + '"> </div> <div class="mini_footer"> <table class="mini_table"> <tr> <td align="left"> <b>Bokförd</b> </td> <td align="right"> <b>Verifikat</b> </td> </tr> <tr> <td align="left">' + booked + '</td> <td align="right">' + verification + '</td> </tr> </table> </div> </div>';
    }
    else {
	printMiniResult += '<div class="mini" id="\'' + id + '\'" onclick="changeInfo(\'' + arrived + '\', \'' + booked + '\', \'' + verification + '\', \'' + org + '\', \'' + price + '\', \'' + stax + '\', \'' + id + '\'), getPDF2(\'' + id + '\')"> <div class="mini_header2"> <table class="mini_table"> <tr> <td align="left"> <b>Inkommen</b> </td> <td align="right"> <b>ID</b> </td> </tr> <tr> <td align="left">' + arrived + '</td> <td align="right">' + id + '</td> </tr> </table> </div> <div class="mini_img"> <img src="data:image/jpeg;base64,' + pic + '"> </div> <div class="mini_footer2"> </div> </div>';
    }
}


As an additional change (as prompted by the answer from vol7ron) you could use an array, instead of string concatenation. As vol7ron says, this would be far more efficient.

So you'd have something like this instead...

var printMiniResult = [];

printAllMini = function() {
  ...
  document.getElementById("mini_blocks").innerHTML = printMiniResult.join("");
  ...
}

printMini = function(id, arrived, booked, verification, org, price, stax, pic) {
  ...
  printMiniResult.push(...);
  ...
}

Upvotes: 1

vol7ron
vol7ron

Reputation: 42149

  1. Is there something that makes the += method slow when creating divs?

Yes, but not just DIVs. String concatenation is expensive and, in general, yields bad performance. You begin to notice this performance when string grows in length, or you perform more operations (concatenations).

  1. Is there a reason why none of the divs shows up until the very end?

Explain.

  1. I'm new to web-programming. Is there better ways to do the same thing?

You could use various techniques of cloning objects, or only creating the string once. If you want to get into better front-end solutions, you should look at web frameworks/libraries, such as React or Angular.

An example of how to improve just using your code is to forego the string concatenation in the loop by building an array. You can then concatenate all elements of the array afterwards by joining on an empty string: (e.g. ['begin','end'].join(''))

Upvotes: 2

Related Questions