im having table with more than 1k rows and more than 7 columns, trying to parse into array object, i tried using jquery
$(tableSearch).each(function () {
$('tr', $(this)).each(function (key, tr) {
var self = this;
var obj = new Object();
var rowPos = 0;
$('td', tr).each(function (rowPos) {
obj[_self.colModel[rowPos].name] = $(this).html();
});
obj['Key'] = 'Rec-' + key;
});
});
in FF it takes 300 milli seconds, but in IE its taking 60 seconds :(
as u can compare its around 200 times slower.
is there any way to get performance in IE. i tried raw javascript methods also still in IE efficiency is not achieved.
help me!!!!!!!.. how can i get similar performance in all browsers.
THANKS in Advance
i got the solution IE can be faster if we implemented this way
table = document.getElementById("myGrid");
for (i = 0; i < table.rows.length; i += 1) {
var rowData = new Array();
row = table.rows[i];
for (j = 0; j < row.cells.length; j += 1) {
cell = row.cells[j];
rowData[j] = cell.innerHtml
}
obj.push(rowData);
}
Note: disable debug mode if ur using any debugger.
then IE seems to be reasonable but cant be faster than FF or safari
Related
Every other browser gets through this JS almost instantly, see below benchmarks
It is slowing down only on this code in a function (rest runs instant), which is so simple its making me confused as to what I can do to fix it.
I cannot reproduce this in JSFiddle, here is the equivalent code https://jsfiddle.net/5ax7mshz/ . I can see with performance.now() that this is the only code slowing it down in our app, and its a purely JS+JQ app, there are no other variables here...somehow it takes 600x as long in our app as it does in the fiddle. Very much at my wits' end here.
Thanks All!
var options = "";
for (var i = 0; i < data.Vendor.length; i++) {
options += "<option value='" + data.Vendor[i].VendorID + "'>" + data.Vendor[i].Name + "</option>";
}
$el.append(options);
Actual benchmarking values
Average Edge benchmark (trying some of the solutions here so far unfortunately still resulted in similar values)
28155 ms total
6968 ms list1 (2232 items)
21179 ms list2 (4016 items)
7.6 ms list3 (10 items)
Here's Chrome if you want some laughs
55.07 ms total
21.09 ms list1 (2232 items)
32.18 ms list2 (4016 items)
1.79 ms list3 (10 items)
Aaaand Firefox takes 46ms total
Is the append you're using from jQuery?
I don't have Edge on this machine, but I do have IE, so I'll use that as a reasonable equivalent. I tried several 4000 select-option builds:
String concat followed by single jQuery append
jQuery append per entry
appendChild per element (no jQuery)
add new Option(... to select.options (no jQuery)
For timing I'm using
console.time('build');
build();
console.timeEnd('build');
And the results, two runs - fastest time (ms):
IE11 805 2120 605 6033
FF80 48 190 26 21
CR84 95 342 52 214
With 4000 elements, this is comparison to your list2, while the first timing number should match your results (although note I made up the value/text per entry, since I don't have access to your list). Note Chrome is significantly slower than yours (95 vs 32 - 3x), and IE is significantly faster (805 vs 2117 - 26x). For my Chrome process I have many open tabs, but there's also an update waiting, for my IE process I have nothing else open, but it's also not exactly the same as Edge (assuming you're not using Webkit Edge - if you are, then it's a very different engine)
The jQuery based builds are both poor performers. This isn't really a fair fight, since this method has to parse a string and interpret it as HTML. Build 4 also did poorly on everything but FireFox. So, it looks like vanilla javascript with appendChild is the best approach.
To map this back to your data (which may have further object access delays):
function build(data, select) {
var n=data.Vendor.length;
for (var i=0; i<n; i++) {
var item=data.Vendor[i];
var opt=document.createElement("option");
opt.value=item.VendorID;
opt.innerText=item.Name;
select.appendChild(opt);
}
}
// Call egs:
// build(data, document.getElementById("itemList"));
// build(data, document.forms[0].itemSelect);
ended up doing this to at least make it 5-7 seconds total (5000-7000ms) instead of 28, which is 'ok' as this only affects a couple clients, rest use Chrome (50ms) and FF (40ms) and IE11+ (300ms)
document.getElementById("vendor").innerHTML = "";
var i = 0;
var loop_num = 0;
var vendorCount = data.Vendor.length;
var vendorPiece;
var k = 0;
var options = [];
if (isEdge) {
//add optgroups to select
var num_selects = Math.ceil(vendorCount / 1000); //say, 3 for 2323
for (var jj = 1; jj <= num_selects; jj++) {
var optgroup = document.createElement("optgroup");
optgroup.label = "";
optgroup.id = "vendor" + jj;
document.getElementById("vendor").appendChild(optgroup);
if (jj == 1) {
var blankopt = document.createElement("option");
blankopt.value = "";
blankopt.text = "";
document.getElementById("vendor" + jj).appendChild(blankopt);
}
}
} else {
var blankopt = document.createElement("option");
blankopt.value = "";
blankopt.text = "";
document.getElementById("vendor").appendChild(blankopt);
}
while (i < vendorCount) {
var pieceEndIndex =
Math.min(1000, vendorCount - i) == 1000 ? i + 1000 : vendorCount;
k = 0;
options = [];
vendorPiece = data.Vendor.slice(i, pieceEndIndex);
for (var j = 0; j < vendorPiece.length; j++) {
var vendor = vendorPiece[j];
options[k++] = "<option value='";
options[k++] = vendor.VendorID;
options[k++] = "'>";
options[k++] = vendor.Name;
options[k++] = "</option>";
}
var vendor_id = !isEdge ? "vendor" : "vendor" + (loop_num + 1);
document.getElementById(vendor_id).innerHTML += options.join("");
i += vendorPiece.length;
loop_num++;
}
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 need to populate eight selectObject pulldown objects on a page with several thousand (8192) items each. I'm currently doing this in Javascript the only way I know how:
var iCount;
var option1;
var selectObject1 = document.getElementById('ifbchan');
for(iCount = 0; iCount < 8192; iCount++)
{
option1=document.createElement("option");
option1.text = "Out " + iCount;
option1.value=iCount;
try
{
selectObject1.add(option1, selectObject1.options[null]);
}
catch (e)
{
selectObject1.add(option1, null);
}
}
selectObject1.selectedIndex = 0;
This method works properly but is extremely slow! Each of these 8K loops takes something like 10 seconds to complete. Multiply by 8 different loops and the problem is obvious. Is there any other way to add large numbers of items to a drop down list that would be faster? Any faster alternatives to the drop down control for presenting a large list of items? Thanks for any ideas.
~Tim
I'd try the following:
var elements = ""
var i;
for(i= 0; i < 8192; i++){
elements += "<option value='"+ i + "'>Out " + i + "</option>";
}
document.getElementById("ifbchan").innerHTML = elements;
This way you only perform one action on the DOM per loop not 8000+.
Oh and here's one I prepared earlier: http://jsfiddle.net/3Ub4x/
Few things before the answer.
First of all I do not think that the best way to do this is a server side implementation. If you can do something on the client you should do this and not touch your server (if it is not security related).
Second thing - why exactly do you need 8000 elements in select list? Think as a user of your app, who would like to scroll through 8000 elements just to select his element? As it was mentioned before - autocomplete sounds much more suitable.
And right now is an answer:
Your original approach is here: it takes approximately 1724 miliseconds to complete for 10000 elements (You can see this by running the script and checking inspector).
var start = new Date();
var n = 10000;
var iCount;
var option1;
var selectObject1 = document.getElementById('ifbchan');
for(iCount = 0; iCount < n; iCount++)
{
option1=document.createElement("option");
option1.text = "Out " + iCount;
option1.value=iCount;
try
{
selectObject1.add(option1, selectObject1.options[null]);
}
catch (e)
{
selectObject1.add(option1, null);
}
}
selectObject1.selectedIndex = 0;
var time = new Date() - start;
console.log(time);
I do not like a lot of this code (it is too many lines) so I will rewrite it in jquery.
var start = new Date();
var n = 10000;
for (var i = 0; i<n; i++){
$("#ifbchan").append("<option value="+i+">"+i+"</option>")
}
var time = new Date() - start;
console.log(time);
The next fiddle is here. Much less lines, and some time improvement. Now it is 1312 milliseconds. But it append new element in every loop.
The next fiddle get rid of this.
var start = new Date();
var n = 10000;
var html = '';
for (var i = 0; i<n; i++){
html += "<option value="+i+">"+i+"</option>";
}
$("#ifbchan").append(html);
var time = new Date() - start;
console.log(time);
Wow, now it is only 140 milliseconds.
for (var i = 0; i<n; i++){
select.append('<option value='+i+'>'+i+'</option>');
}
Beware, this doesn't work in IE. See this link -
Using innerHTML to Update a SELECT – Differences between IE and FF
How could I populate a second select element? I've figured out how to do the first one. But how could I do the same for the second depending on which "Make" is selected? I've tried to talk myself through it while taking small steps but I'm thinking this may be too advanced for me.
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"},{"name":"Spider","value":"22172"}]}';
var carobj = eval ("(" + cars + ")");
var select = document.getElementsByTagName('select')[0];
//print array elements out
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
select.options.add(new Option(d.name, i))
};
If I read your question right, you want to populate a second select with the models for the make in the first select. See below for a purely JS approach (with jsfiddle). If possible, I would recommend looking into jQuery, since I would prefer a jQuery solution.
http://jsfiddle.net/m5U8r/1/
var carobj;
window.onload = function () {
var cars = '{"USED":[{"name":"Acura","value":"20001","models":[{"name":"CL","value":"20773"},{"name":"ILX","value":"47843"},{"name":"ILX Hybrid","value":"48964"},{"name":"Integra","value":"21266"},{"name":"Legend","value":"21380"},{"name":"MDX","value":"21422"},{"name":"NSX","value":"21685"},{"name":"RDX","value":"21831"},{"name":"RL","value":"21782"},{"name":"RSX","value":"21784"},{"name":"SLX","value":"21879"},{"name":"TL","value":"22237"},{"name":"TSX","value":"22248"},{"name":"Vigor","value":"22362"},{"name":"ZDX","value":"32888"}]},{"name":"Alfa Romeo","value":"20047","models":[{"name":"164","value":"20325"},{"name":"8c Competizione","value":"34963"}, {"name":"Spider","value":"22172"}]}]}';
carobj = eval ("(" + cars + ")");
var makes = document.getElementById('make');
for (var i = 0; i < carobj.USED.length; i++) {
var d = carobj.USED[i];
makes.options.add(new Option(d.name, i));
}
makes.onchange = getModels;
getModels();
}
// add models based on make
function getModels () {
var makes = document.getElementById('make');
var make = makes.options[makes.selectedIndex].text;
for (var i = 0; i < carobj.USED.length; i++) {
if (carobj.USED[i].name == make) {
var models = document.getElementById('model');
models.options.length = 0;
for (var j= 0; j < carobj.USED[i].models.length; j++) {
var model = carobj.USED[i].models[j];
models.options.add(new Option(model.name, j));
}
break;
}
}
}
I would also recommend looking into safer JSON parsing. There is a security risk in using eval if it runs on any user input. You could look into JSON.org and their json2.js. Or if you want to use jQuery: parseJSON. Below is the jQuery version:
jQuery.parseJSON(jsonString);
JSON parsing tips from: Safely turning a JSON string into an object.
I have been working on a scheduling website for the past few weeks. I am showing the schedules as PHP generated html-tables. I use merged cells for showing events. I have come to a problem when trying to delete events using JS. Since those are merged cells, using rowspan, I have to go through the table and re-adding empty cells whenever there is a need when I delete one. My solution is working fine for when my table contains one merged cell among nothing but empty cells, but with a more complex table, it fails. I can't really grasp what's wrong with it, except that it doesn't correctly find the cellIndex anymore. Does anyone have a clue? Here is what I'm talking about:
http://aturpin.mangerinc.com/table.html
(Click on an event to remove it, or attempt to anyhow)
This sample may help you find your solution. It seems to demonstrate your problem as well as have some sample code to generate a matrix to help you solve it.
EDIT: I liked the puzzle and decided to play with it for a bit, here is a "functioning" example of implementing that sample (although sometimes the table doesn't seem to redraw correctly. This should probably help you get further along the way.
function getTableState(t) {
var matrix = [];
var lookup = {};
var trs = t.getElementsByTagName('TR');
var c;
for (var i=0; trs[i]; i++) {
lookup[i] = [];
for (var j=0; c = trs[i].cells[j]; j++) {
var rowIndex = c.parentNode.rowIndex;
var rowSpan = c.rowSpan || 1;
var colSpan = c.colSpan || 1;
var firstAvailCol;
// initalized the matrix in this row if needed.
if(typeof(matrix[rowIndex])=="undefined") { matrix[rowIndex] = []; }
// Find first available column in the first row
for (var k=0; k<matrix[rowIndex].length+1; k++) {
if (typeof(matrix[rowIndex][k])=="undefined") {
firstAvailCol = k;
break;
}
}
lookup[rowIndex][c.cellIndex] = firstAvailCol;
for (var k=rowIndex; k<rowIndex+rowSpan; k++) {
if(typeof(matrix[k])=="undefined") { matrix[k] = []; }
var matrixrow = matrix[k];
for (var l=firstAvailCol; l<firstAvailCol+colSpan; l++) {
matrixrow[l] = {cell: c, rowIndex: rowIndex};
}
}
}
}
// lets build a little object that has some useful funcitons for this table state.
return {
cellMatrix: matrix,
lookupTable: lookup,
// returns the "Real" column number from a passed in cell
getRealColFromElement: function (cell)
{
var row = cell.parentNode.rowIndex;
var col = cell.cellIndex;
return this.lookupTable[row][col];
},
// returns the "point" to insert at for a square in the perceived row/column
getPointForRowAndColumn: function (row,col)
{
var matrixRow = this.cellMatrix[row];
var ret = 0;
// lets look at the matrix again - this time any row that shouldn't be in this row doesn't count.
for (var i=0; i<col; i++)
{
if (matrixRow[i].rowIndex == row) ret++;
}
return ret;
}
};
}
function scheduleClick(e)
{
if (e.target.className != 'event')
return;
//Get useful info before deletion
var numRows = e.target.rowSpan;
var cellIndex = e.target.cellIndex;
var rowIndex = e.target.parentNode.rowIndex;
var table = e.target.parentNode.parentNode;
var tableState = getTableState(table);
var colIndex = tableState.getRealColFromElement(e.target);
//Deletion
e.target.parentNode.deleteCell(cellIndex);
//Insert empty cells in each row
for(var i = 0; i < numRows; i++)
{
var row = table.rows[rowIndex + i];
row.insertCell(tableState.getPointForRowAndColumn(rowIndex+i, colIndex));
}
}