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);
}
Related
I'm trying to dynamically create a table using JS and jQuery. (I know I can hard code it in html but I don't want to do that.)
I have an extra row at the table that I'm having trouble removing.
Can someone please provide an answer/answers on how best to solve this in my code?
My code is posted below.
var game = {
matrix: [],
startGame: function()
{
this.doDomStuff(); //build board
},
doDomStuff: function(row, column)
{
for(var i = 0; i < 7; i++)
{
var row = $('<div>');
row.attr('id', 'data-row' + (i + 1));
row.addClass('data-row');
for(var j = 0; j < 6; j++)
{
var column = $('<div>');
column.attr('id', 'data-column' + (j + 1));
column.addClass('data-column');
column.addClass('column');
row.append(column);
}
$('body').prepend(row);
}
}
};
If you are making a game, and each row can have its own logic and behaviour, so it will be faster to operate array neither DOM structure. If your logic will be more complecated, you will see the difference in perfomance. So, call the DOM structure if you are really need to update objects there. For example:
function SuperTable(){
this.tablerows = [];
this.DOMObject = $("body");
}
SuperTable.prototype.AddRow = function() {
this.tablerows[this.tablerows.length] = new SuperRow(this.tablerows.length,this.DOMObject);
}
SuperTable.prototype.RemoveRow = function(rowIndex) {
this.tablerows[rowIndex].DOMObject.remove(); // remove element from DOM
this.tablerows.splice(rowIndex,1); // remove element from logic of the game
}
function SuperRow(rownumber,parent) {
this.DOMObject = $("<div>");
this.DOMObject.addClass('data-row');
parent.prepend(this.DOMObject);
}
mytable = new SuperTable()
mytable.AddRow() // create row on 0 position
mytable.RemoveRow(0) // remove row from 0 position
You can use JQuery's .remove() function.
For example, if you want to remove the last row: $("#data-row7").remove()
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 can't seem to figure out what I'm doing wrong on this code I wrote yesterday. This was my first time whipping up JavaScript, and using jQuery and Node.js also both for the first time, and I would think this three dimensional array should work as is. I've seen confusing mention about what multidimensional arrays are and people saying that JavaScript does not have any, though it has arrays of arrays. Anyways I guess I'm technically using an array of an array of an array, and don't understand why my outer array, which I imagined being an outer dimension by design, over-writes elements from the two inner-arrays into its own elements. The two inner arrays appear to work as they are supposed to, but the outter-most array mixes up the data in some way I don't really understand.
The inconsistency/problem can be observed by scrolling through the output.json file that is generated by this code and seeing that the outputs clearly do not match up with each of the three tables on this webpage I'm scraping from:
// My server.js file:
var express = require('express');
var fs = require('fs');
var request = require('request');
var cheerio = require('cheerio');
var app = express();
// the link below is a tutorial I was loosely following
// see http://scotch.io/tutorials/javascript/scraping-the-web-with-node-js
app.get('/scrape', function(req, res) {
url = 'http://espn.go.com/nba/player/stats/_/id/4145/kareem-abdul-jabbar'
request(url, function(error, response, html) {
if(!error) {
// utilize the Cheerio library on the returned html, yielding jQuery functionality
var $ = cheerio.load(html);
var numOfRows;
var stats = [[[]]];
for(var chart = 0; chart < 3; chart++) {
stats.push([[]]); // allocates space for each grid on each chart (each set of rows and columns)
$('.tablehead').eq(chart).filter(function(){
var data = $(this);
numOfRows = data.children().length - 2;
for(var i = 0; i < numOfRows + 1; i++) {
stats[chart].push([]); // allocates space for each row in the chart
}
})
var numOfColumns;
$('.stathead').eq(chart).filter(function(){
var data = $(this);
stats[chart][0][0] = data.children().first().text();
})
$('.colhead').eq(chart).filter(function(){ // first() specifies to select the first of the three occurances of this class; use eq(param) to find the Nth occurance
var data = $(this);
numOfColumns = data.children().length;
for(var i = 0; i < numOfColumns; i++) {
stats[chart][1][i] = data.children().eq(i).text();
}
})
var currentRow = 2;
for(var oddRow = 0; oddRow < (numOfRows + 1)/2 - 1; oddRow++) {
$('.tablehead .oddrow').eq(oddRow).filter(function(){
var data = $(this);
for(var c = 0; c < numOfColumns; c++) {
stats[chart][currentRow][c] = data.children().eq(c).text();
}
currentRow += 2;
})
}
currentRow = 3;
for(var evenRow = 0; evenRow < (numOfRows + 1)/2 - 1; evenRow++){
$('.tablehead .evenrow').eq(evenRow).filter(function(){
var data = $(this);
for(var c = 0; c < numOfColumns; c++) {
stats[chart][currentRow][c] = data.children().eq(c).text();
}
currentRow += 2;
})
}
currentRow -= 1; // gets the last allocated row index (after "currentRow += 2" has been executed)
$('.tablehead .total').eq(chart).filter(function(){
var data = $(this);
var LOGOIDX = 1;
for(var c = 0; c < numOfColumns - 1; c++) {
if(c < LOGOIDX) {
stats[chart][currentRow][c] = data.children().eq(c).text();
}
if(c == LOGOIDX) {
stats[chart][currentRow][c] = "N.A.";
stats[chart][currentRow][c + 1] = data.children().eq(c).text();
continue;
}
else {
stats[chart][currentRow][c + 1] = data.children().eq(c).text();
}
}
})
} // end chart loop
}
// Want to parse my json so that it displays in format: "name: value" rather than just "name" as it is now...
fs.writeFile('output.json', JSON.stringify(stats, null, 4), function(err){
console.log('File successfully written! - Check the project directory for the output.json file');
console.log('Number of columns in chart is: ' + numOfColumns);
})
// message to browser reminding that there's no UI involved here.
res.send('Check the console!')
})
})
app.listen('8081')
console.log('Magic happens on port 8081');
exports = module.exports = app;
Aha! Caught my bug -- just a simple logical error. Kind of embarrassing I didn't see it earlier, but oh well, got some practice and research (and a fair amount of distraction) by the end of the day:
As it can be seen, all the searching for HTML classes I did were parameterized by a variable named "chart", except for where I searched for odd rows and even rows within each chart -- the actual scraping of the bulk of each chart, so it naively appeared that my "3d array [was] over-writing values from other dimensions" <-- lol.
In simple, I just needed to create an offset based on a condition for each chart (a few extra lines of code), and I needed to edit two lines of code to reflect the newly calculated offset like so:
$('.tablehead .oddrow').eq(rowOffset + oddRow).filter(function(){
and
$('.tablehead .evenrow').eq(rowOffset + evenRow).filter(function(){
Thanks anyways for any help! I hope this issue tangentially benefits others greatly : P
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
Which is the best way to find all elements from page which match the size i need, and less browser overloading ?
the most important thing is to make it so that the browser will not overload and will not crash if on page are a lot of elements.
what i'm using now is:
elements = document.getElementsByTagName("*");
len = elements.length;
for(var i = 0; i < len; i++)
if(elements[i].clientHeight == MyHeight && elements[i].clientWidth == MyWidth)
{ /*my code*/ }
Please, if anybody knows a better way which don't stress the browser as much as what I'm using now, then this is what i need.
If you absolutely have to do it, it would be more efficient to iterate through the tree recursively, because then once you hit an element that is less than what you need, you know nothing inside of it can be the required size. Still seems like there would be a better solution under most circumstances, but I don't know your specific problem.
EDIT:
This works.
function findElements( width, height, element ){
var results = Array();
for( var i=0; i<element.childNodes.length; i+=1 ){
var childElement = element.childNodes[i];
if( childElement.clientWidth == width && childElement.clientHeight == height ){
results.push(childElement);
results = results.concat(findElements(width, height, childElement));
} else if( childElement.clientWidth < width || childElement.clientHeight < height ){
continue;
} else {
results = results.concat(findElements(width, height, childElement));
}
}
return results;
}
findElements(myWidth, myHeight, document);
I think we are talking about speed here.(though the difference is marginal).
Who is the fastest??? -->click the run button and test it your self!!!
I guess document.body.querySelectorAll("*"); is a clear winner.
var items = document.body.querySelectorAll("*");
var target, i, len = items.length;
for (i=0;i<len;i++) {
target=items[i];
if(//your condition)
{ and the code}
**Conclusion:**Just write code which are understandable,readable and elegant.I think understanding the logic is the most trivial part.
UPDATE:
function findParentNode() {
var testObj = document.body.querySelectorAll("*");;
for(var i=0;i<=testObj.length;i++){
if(testobj.offsetWidth=="your width" && testobj.offsetHeight=="your height"){
//push it in an array or do whatever you wanted to do
}
else{
//no elements found!!!
}
}
}