Using jQuery .append I write some html to form a 10,000px grid of 125px X 80px. Where the pixels are numbered first down then across. Now this works fine but is slow enough that there's noticeable lag loading the page compared to writing it straight in html. Is it possible to speed this up at all while still maintaining the pixel numbering?
My html is:
<div id="grid">
</div>
Javascript:
function createGrid() {
var counter = 1;
var rowCounter = 1;
var divs = 10000;
$('<table width="625px"><tr>').appendTo('#grid');
for (var i = 1; i <= divs; i++) {
if (i % 125 == 0 ){
$('</ tr><tr>').appendTo('#grid');
rowCounter++;
counter = rowCounter;
}
else
$('<td id="pixel_' + counter + '" class="pixel"></td>').appendTo('#grid');
counter =+ 80;
}
$('</tr></table>').appendTo('#grid');
}
Your code won't work as you expect it to, because .append() creates complete DOM elements. $('<table width="625px"><tr>').appendTo('#grid') will automatically close both tags, and you'll have to append the next row to the table, and the cell to the row.
As it happens, it's inefficient to constantly append elements to the DOM anyway. Instead, build the table as a single string and write it out all at once. This is more efficient since you're only adding to the DOM one time.
function createGrid() {
var counter = 1;
var rowCounter = 1;
var divs = 10000;
var tstr = '<table width="625px"><tr>';
for (var i = 1; i <= divs; i++) {
if (i % 125 == 0) {
tstr += '</ tr><tr>';
rowCounter++;
counter = rowCounter;
} else
tstr += '<td id="pixel_' + counter + '" class="pixel"></td>';
counter = +80;
}
tstr += '</tr></table>';
$('#grid').append(tstr);
}
http://jsfiddle.net/mblase75/zuCCx/
$('<table width="625px"><tr>')
is not the same as writing and appending an HTML string! jQuery will evaluate that <table><tr> string and create a DOMElement from it. I.e., with just this tiny bit of code, you have created a whole table in the DOM. The closing tags are auto-completed and the table is instantiated. From then on you need to work with it as a DOM object, not as a string to append to.
Your code is probably slow because you're creating tons of incomplete/autocompleted tiny DOM objects which are all somehow being bunched together, probably not even in the correct structure. Either manipulate DOM objects, which should be pretty fast, or construct a complete string and have it evaluated once.
One of the first steps towards improving performance would be generating the complete HTML and appending to the DOM in one step.
function createGrid() {
var counter = 1;
var rowCounter = 1;
var divs = 10000;
var html = '<table width="625px"><tr>';
for (var i = 1; i <= divs; i++) {
if (i % 125 == 0 ){
html += '</ tr><tr>';
rowCounter++;
counter = rowCounter;
}
else
html += '<td id="pixel_' + counter + '" class="pixel"></td>';
counter =+ 80;
}
html += '</tr></table>';
$('#grid').html(html);
}
Related
I am using this for loop in some code. This code appends all the elements present in "left array" in a table and assigns a unique rowID to them which is working perfectly fine.
Problem is: This loop takes around 9 seconds to execute when length of "left" array is 5000,but when length of "left" array is 10000 and 15000, it takes 27 seconds and 54 seconds respectively. I guess this is an ambiguous behavior, the time should increase linearly with increase in number of iterations. I would be glad if someone could tell me the reason and the solution to this problem.
Thanks in advance. :)
$("body").append('<p id = "p1">table1</p>')
table = $('<table id="first-table">');
div = $('<div ></div>');
$(div).append(table);
$('body').append(div);
for (var rowID = 0, l = left.length; rowID < l; rowID++) {
if (left[rowID] != null) {
var tr = $('<tr>');
table.append(tr);
tr.append('<td class="cell" id="a' + rowID + '">' + left[rowID] + '</td>');
}
}
Appending N elements individually causes up to N reflow's. As the no of elements to be added increases it decreases performance.
You can refactor your code, create the DOM fragment and nodes in memory then use single append()
//Createing Nodes in memory
var table = $('<table id="first-table">');
for (var rowID = 0, l = left.length; rowID < l; rowID++) {
if (left[rowID] != null) {
$('<tr>').append('<td class="cell" id="a' + rowID + '">' + left[rowID] + '</td>')
.appendTo(table);
}
}
var div = $('<div ></div>').append(table);
var paragraph = $('<p id = "p1">table1</p>');
//Append to DOM
$("body").append(paragraph.add(div))
A good read What is DOM reflow?
I'm new to web development. I need to output all numbers ranging from 1-100, each on a new line. The following code only outputs the last number:
var main = function() {
for(var i = 1; i <= 100; i++) {
$(".results").append("<p>").text(i);
}
};
$(document).ready(main);
While, this does exactly what I want it to:
var main = function() {
var $content;
for(var i = 1; i <= 100; i++) {
$content = $("<p>").text(i);
$(".results").append($content);
}
};
$(document).ready(main);
Why does the second solution work but not the first?
The issue with the first is that append() returns the parent element (the .results element) not the p which was appended to it. You are therefore updating the text property of .results on each iteration.
Note also that append() is a relatively expensive operation in terms of performance, so you can improve execution speed by rearranging your logic to only append once:
var main = function() {
var content = '';
for (var i = 1; i <= 100; i++) {
content += '<p>' + i + '</p>';
}
$(".results").append(content);
};
I've an ajax-call that will give me a 500 row return. Each row will create a HTML-object that will be added to the DOM. This all works fine, but it's slow.
I would like to add 20, then render what is done, and then continue to add the last 480. However, I can't figure out how to force rendering.
The code is something like this:
for (i = 0; i < 500; i += 1) {
$(newdata[i]).insertAfter('#object');
}
Where newdata is a textstring, for example
"<p>hello world</p>"
Edit
I might have left out some critical information in my post. The nodes are not to be inserted in order. It's a tree and each node has a parent that I know about. And each parent is garanteed to be inserted before the node. So I can't just append nodes after eachother since they might be in different branches.
Stop inserting one node at the time, insert collections of nodes instead.
It's not the loop that's slow, it's DOM manipulation that is slow, and inserting 500 DOM nodes one node at the time will be slow.
var nodes = $();
for (i = 0; i < 20; i++) {
nodes.append(newdata[i])
}
$('#object').after(nodes);
var more_nodes = $();
for (i = 20; i < 500; i++) {
more_nodes.append(newdata[i])
}
$('#object').after(more_nodes);
If you do it like this, it will probably be ten times faster, and you don't have to insert 20, then 480 etc.
Give the rendering code time to run. Write a few rows, call setInterval() to let other code run, and continue:
function renderLines(newdata) {
var len = newdata.length;
var sofar = 0;
var obj = $('#object');
var renderSome = function() {
for ( var i = 0; (i < 20) && ((i + sofar) < len); ++i )
{
$(newdata[i + sofar]).insertAfter(obj);
}
sofar += 20;
if (sofar < len)
setTimeout(renderSome, 10);
};
setTimeout(renderSome, 10);
}
I'm currently trying to dynamically create a table using jQuery, JavaScript and HTML. In my application images are coming dynamically.
**CASE:**If 9 images are coming dynamically then there must 3 rows and 3 columns, and if 16 image coming then 4 rows and 4 columns and so on. I also want some solution if odd number images(eg: 17, 19, 91..etc). I have searched lot but I didn't got proper answer. I am not looking for plugins.
Again, I'm not sure if this is what you wanted since your question is vague, no one has any idea how your markup looks like or how are you getting the images but I guess one of the solutions might be something like this:
http://jsfiddle.net/Udf3D/
$(document).ready(function(){
var count = $("#container img").size();
var columns = Math.sqrt(count);
columns = parseInt(columns);
$("#container img").css('width', $("#container").width() / columns);
});
The idea is to get the number of images, find square root of that number and then simply set the width of the image to fit nicely in the container. (if you have 25 images, you'll have 5 columns and image width would be 1/5 of the contaier width).
Again no idea if this fits your needs but it's a direction in which I would go.
Here's another possibility that assumes fixed with of images. This means as the number of images gets large, you may go over the edge of the page. Maybe that is ok.
http://jsfiddle.net/C4WpQ/14/
function createImages (n)
{
var images = [];
for (i = 0; i < n; i++)
{
var imageUrl = "http://images.fake.com/" + i + ".png";
images.push(imageUrl);
}
return images;
}
function leastSquareRoot (n)
{
// maybe use ceil if you want a wider rectangle vs a taller one
// when a square is not possible
var sr = Math.sqrt(n);
return Math.floor(sr);
}
function createRowsAndCols (images, width)
{
var result = "<table>\n";
var lsr = leastSquareRoot(images.length);
for (i = 0; i < images.length; i++)
{
if (i % width == 0)
{
result += "\t<tr>\n";
}
result += "\t\t<td><img src=\"" + images[i] + "\"></td>\n";
if (i % width == width - 1 || i == images.length - 1)
{
result += "\t</tr>\n";
}
}
result += "</table>\n";
return result;
}
function showTags(markup)
{
return markup.replace(/\</g, '<').replace(/\>/g, '>');
}
$( document ).ready(function() {
$("#canvas").html("Creating images...");
var images = createImages(17);
$("#canvas").append(" created " + images.length + " images.<br/>");
var width = leastSquareRoot(images.length);
$("#canvas").append("The proposed width is " + width + ".<br/>" );
var result = createRowsAndCols(images, width);
$("#canvas").append(result);
$("#canvas").append("<pre><code>" + showTags(result) + "</code></pre>");
});
I have the following jQuery statement in a loop. #MainContent_gvDemographic and #tblFreez are two tables in a page.
$("#MainContent_gvDemographic").find(str)
.css("height", $("#tblFreez")
.find(str)
.css("height"))
When there are many steps in the loop, it takes a very long time to complete. To fix the problem, I then use two loops, one for reading the height of $("#tblFreez").find(str), the other for writing the height into $("#MainContent_gvDemographic").find(str), and use an array to carry the height data between the two loops. It becomes much faster now. Does anyone know why the two solutions have such big difference in performance? The computational complexity looks the same to me.
All right, here are the two complete version.
Original:
function FixHeight() {
var rowCount = $('#tblFreez tr').length;
for (var i = 0; i < rowCount; i++) {
var str = "";
if ($.browser.msie) {
str = "tr:eq(" + i + ") td";
}
else {
str = "tr:eq(" + i + ")";
}
$("#MainContent_gvDemographic").find(str).css("height", $("#tblFreez").find(str).css("height"));
}
}
New:
function FixHeight() {
var rowCount = $('#tblFreez tr').length;
var hei = new Array();
for (var i = 0; i < rowCount; i++) {
var str = "";
if ($.browser.msie) {
str = "tr:eq(" + i + ") td";
}
else {
str = "tr:eq(" + i + ")";
}
hei[i] = $("#tblFreez").find(str).css("height");
}
for (var i = 0; i < rowCount; i++) {
var str = "";
if ($.browser.msie) {
str = "tr:eq(" + i + ") td";
}
else {
str = "tr:eq(" + i + ")";
}
$("#MainContent_gvDemographic").find(str).css("height", hei[i]);
}
}
Why not use only one loop and not for but jQuery .each(). I haven't tested code below, but should work.
function FixHeight() {
var $MainContent = $("#MainContent_gvDemographic");
var $tblFreezRows = $("#tblFreez tr");
var hei, $row;
$tblFreezRows.each(function(index, elem){
$row = $(this);
if ($.browser.msie) {
hei = $row.find('td').css("height");
$MainContent.find("tr:eq(" + index + ") td").css("height", hei);
}
else {
hei = $row.css("height");
$MainContent.find("tr:eq(" + index + ")").css("height", hei);
}
});
}
The DOM operations are usually the expensive operations.
Your first version has heavy DOM operation, but your second version has the count. It looks like load vs number.
Ideally, your first version should be faster as it is just one loop and the number of iteration is half than the second version.. but it is not always the case.
Assume your memory is like 1000M in which 300M can be garbage collected meaning it can be cleaned up. So the Operating systems memory model would call the garbage collector whenever your memory gets closer to 1000M. With those conditions in mind, lets say in your first version, every 5 iterate takes 1000M, which would end up calling garbage collector to clean up or free up resources for next iteration. So if you end up running for 100 iteration which is equals to 100 iteration + 20 times GC processing.
In your second case, assume it take 50 iteration to fill up 1000M, so you would end up calling 4 time GC processing which is basically 20 vs 4 times calling some other process inbetween each of your iterate.
Above is just a speculation and the actual memory modules are much smarter than what I have explained, but that is just to give an idea of load vs numbers.
Anyways..Try below code and see if it fixes your problem,
Setting height at TR level
var fTable = document.getElementById('#tblFreez');
$("#MainContent_gvDemographic tr").each(function(ridx) {
$(this).height($(fTable.rows[ridx]).height());
});
Setting height at TD level
var fTable = document.getElementById('#tblFreez');
$("#MainContent_gvDemographic tr").each(function(ridx) {
$(this).find('td').each(function (cidx) {
$(this).height($(fTable.rows[ridx].cols[cidx]).height());
});
});
Try detaching the elements before you alter/search them. Then re-append them. DOM-operations are costly.