I have the following piece of code to generate an select list on the fly. The code works okay when the records retrieved are between 0-300 records. However when I try to work with records over this number, 671 records to be exact, the browsers(IE,FF,Chrome,Opera) hang and it seems the javascript is taking up a lot of resources and the browsers become slow after the select option is finally generated after a 5 minute wait....
I am looking for a way to make this more efficient and prevent such hangups as I will work with records upto 5000
$("#idea_selectrel").on("change",function(){
var attributeid = $("option:selected",this).val();
$.post("/OCSRM/PopulateHTMLTablesServlet.do",{"attributeid":attributeid,"table":'idearelations'},function(data){
if(!$.isEmptyObject(data))
{
$("#idea_selectrelvalue option:not(:first)").remove();
var html='';
var len = data.length;
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
$("#idea_selectrelvalue").append(html).show();
}
}
else
{
alert('No data found.');
}
},'json');
//alert(attributeid);
});
Your code is building up a long string of HTML containing the options. It's also appending the string over and over again to the <select> element.
Move the .append() to after the loop:
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
}
$("#idea_selectrelvalue").append(html).show();
Not being able to test the code but from what I can tell you're not clearing the html variable inside the loop, currently, the html will be added recursively for every iteration, and every iteration is updating the DOM, which will get pretty large, fast.
I'm not sure if this is by design, but if it's not, try moving the append outside of the loop.
var html='';
var len = data.length;
for(var i=0,len = data.length; i<len; i++ )
{
var relvalue = [data[i].relvalue];
var description = [data[i].description];
html +='<option value="'+relvalue+'">'+relvalue+' - '+description+'</option>';
}
$("#idea_selectrelvalue").append(html).show();
That is a lot of DOM elements that will be slow to load even if created without JS. Assuming you don't have a way to pre-generate the select on the server and send it down, you could try not appending the value in the loop. You could build out a string of option values and then, after the string is created, set the html of the select to the generated string. The DOM manipulation in the loop could be a culprit at that level. However, it is important to note that large or deeply nested DOM elements can be a performance concern regardless.
Related
I've created a for loop that pulls sales data in columns from an array (salesLog) and places them into a sheet (targetSheet). The sales data is kept in multiple columns. The columns between columnStart and columnEnd are added (unit), matched with the correct row in the source sheet, and placed in the correct row in the targetSheet. This loop works fine except it's too slow, and a fear unscalable with more data. I'm looking for a way to run this loop faster. Any help?
var length = POlistTarget.length;
for (var i=0; i <= length; i++){
//find Row
var row = POlistSource.indexOf(POlistTarget[i]);
//findColumns
var columnStart = periodArr[0]+1
var columnEnd = periodArr.length
var unitArr =
salesLog.getRange(row+3,columnStart,1,columnEnd).getValues().flat().filter(row=>row!="");
//add units in an array
var unit = unitArr.reduce(function(a,b){return a+b;},0);
//execute
targetSheet.getRange(i+4,7,1,1).setValue(unit);
}
Explanation:
As also Tanaike mentioned in his comment, the main improvement is to use setValues once outside of the for loop instead of using setValue inside the for loop iteratively. In this way you will make one call to set the data instead of POlistTarget.length calls. See more details in best practices.
Another improvement both in terms of performance but also code formatting is to use forEach instead of for loop. The idea is to get rid of unnecessary code.
Improved Solution:
const units = [];
POlistTarget.forEach((pt,i)=>{
let row = POlistSource.indexOf(pt);
let columnStart = periodArr[0]+1;
let columnEnd = periodArr.length;
let unit = salesLog.getRange(row+3,columnStart,1,columnEnd).
getValues().
flat().
filter(r=>r!='').
reduce((a,b)=>a+b);
units.push([unit]);
});
targetSheet.getRange(4,7,units.length,1).setValues(units);
More improvements:
If your code was complete, we could see whether it could be feasible to use getRange for the full array on salesLog so you can get rid of getRange inside the for loop. In this way, you could skip the for loop altogether.
I'm new to JSTree, but really want to know how do you traverse jstree to edit each node?
I wrote a recursive function to traverse each nodes, but strange thing to me is,
I take out the data using var treeData =
$('tree').get_json(true);
I put this data into the function , traverse(treeData);
traverse(treeData){ //do some editing work here.
traverse(treeData'schildren);
}
Wired, after the traverse finished, nothing is changed.
I've already set the 'check_callback' to true.
Anyone can help?
BEST regards
Michael
With get_json you only get json object. To change nodes you have to access them. Also you have to use redraw function to update nodes based on changed data.
An example of your functionality could look like:
function traverse(node) {
node.text += ' too';
for (var i=0, len = node.children.length; i<len; i++) {
var childNode = $("#tree").jstree().get_node(node.children[i]);
traverse(childNode);
}
}
var treeData = data.node;
traverse(data.node);
$('#tree').jstree().redraw(true);
Check fiddle - Fiddle
I'm sorry if the title makes no sense. I am attempting to make a text input add strings to an array, which I then want to turn into a visual list. However, when using innerHTML to put the strings into a div, it prints the entire array rather than only the new string being added. Here is my code:
var nameList = [];
function addToList(frm) {
var list = document.getElementById("list");
if(frm.name.value !== "") {
nameList.push(frm.name.value);
} else {
alert("Not a name.");
};
for(var i=0, len=nameList.length; i<len; i++) {
list.innerHTML += "<div>"+nameList[i]+"</div>";
};
}
You can forgo your entire for loop. Everytime you've pushed a name, just add that name to the innerHTML. There is no need to iterate through your entire list everytime.
var nameList = [];
function addToList(frm) {
var list = document.getElementById("list");
if(frm.name.value !== "") {
nameList.push(frm.name.value);
list.innerHTML += "<div>"+frm.name.value+"</div>";
} else {
alert("Not a name.");
};
}
Note: I'm keeping your array assuming that it's used elsewhere outside of the provided snippet.
You're starting at the 0 array index each time you call the function, so it iterates the entire array every time it's called. If you want to only output the newest value when it's added, forget the loop and output frm.name.value each time the function is called.
If you're merely appending the new item to both the array and the HTML when the function is called, you don't need to loop through all items in the array and append them to the HTML; you can just add "<div"+frm.name.value+"</div>" to the HTML.
You'll only need to loop through all the array elements if there's a chance one of them has changed since the last time the function was called.
On an unrelated note, when you're just appending HTML to an element, you'll see a major performance improvement if you use element.insertAdjacentHTML("beforeend",yourHTML); instead of element.innerHTML += yourHTML;. Check out the jsperf test here to see for yourself!
I think your question is not so clear.
I think what you are trying to do is to have the values that have been input so far in an array and at the same time you want to show them on the screen right?
for(var i=0, len=nameList.length; i<len; i++) {
list.innerHTML += "<div>"+nameList[i]+"</div>";
};
The above code that you are running is not right. += is not valid here as it will add whetever html you have already rendered, which is not right.
list.innerHTML = "<div>"+nameList[i]+"</div>";
If you want to do in this way.
Using Javascript, I've pulled a value from an XML document and I'm trying to generate as many tags as the value I've pulled is. I assume I would go about doing this using a for loop, but when I tried this, it would only display one image.
for(i=0; i<red; i++){
document.getElementById("red").innerHTML = "<img src='slike/red.png'></img>";
}
I'm aware of why this isn't working, but I couldn't find a solution myself, so I'm wondering what Javascript function do I need?
Use string concatenation:
html = '';
for(i=0; i<red; i++){
html += "<img src='slike/red.png'></img>";
}
document.getElementById("red").innerHTML = html;
You could also use += when assigning to .innerHTML in the loop, but that's less efficient because the browser has to format and reparse the DIV contents each time. It's better to do it in one fell swoop.
Well, if you think about it logically, it seems that what you've coded is for the innerHTML of red to change "red" number of times. You're not adding the img "red" times, you're just changing it to that one image "red" times.
So, why don't you do this instead:
var numberImages= "";
for(i=0; i<red; i++){
numberImages += "<img src='slike/red.png' />";
}
document.getElementById("red").innerHTML= numberImages;
each time it assigns a single vaue to the innerHTML of the element.
use the below instead.
var elem=document.getElementById("red");
for(i=0; i<red; i++){
elem.innerHTML = elem.innerHTML+"<img src='slike/red.png'></img>";
}
If you're going to be adding a large number of elements to the DOM, you should be using a DocumentFragment to avoid any unnecessary reflows. Here's a quick example (and a fiddle):
var fragment = document.createDocumentFragment();
for (var i = 0; i < 10; i++)
{
var img = document.createElement('img');
img.src = 'slike/red.png';
fragment.appendChild(img);
}
document.getElementById('red').appendChild(fragment);
What this allows you to do is create as many elements as you need and add them to the DOM in batch which will only reflow the DOM once.
I have a database webmethod that I call via Jquery ajax. This returns an array of data objects from the server. For each data object I want to populate a form with maybe a couple dozen fields.
What is the most efficient way to generate and populate these forms.
Right now, I create a string of the html in my javascript code for each record and then add it to the page. Then I add some events to the new elements.
This works OK in firefox, about 700 ms total for an array of 6 elements, and 4500 ms for an array of 30 elements. However, this is an internal app for my company, and the clients can only use IE8 on their machines. In IE8, this takes 2-10 SECONDS! for and array of length 6 and 47 seconds the one time it was actually able to complete for an array of length 30. Not sure what the ##$% IE8 is doing, but anyways... I need something more efficient it seems...
Thanks!
MORE INFO:
Edit: first thing I do is:
$("#approvalContainer").empty();
Web method signature:
[WebMethod]
public List<User> ContractorApprovals()
So I call the method and then do this with the data:
for (var i = 0; i < data.length; i++) {
displayUserResult("#approvalContainer", data[i], false);
}
formEvents("#approvalContainer");
$("#approvalContainer").show("slow");
displayUserResult looks like this:
var div = "<div style=\"width:695px\" class=..........."
fillForm(div,data)
$("#approvalContainer").append(div)
formEvents does things like add click events, create styles buttons and add watermarks.
fillForm does stuff like this:
$(userForm).find(".form-web-userName").text(userObject._web._userName);
where userForm is the div created in the displayUserResult function, and userObject is one of the objects in the array returned by the ajax call.
Please let me know if you need more information.
You are doing too much DOM manipulation. Every .append and .find and .text inside your loop makes it slower.
The most efficient way is to build an entire HTML string and append that once. I'm not going to bother with the displayUserResult function, and just modify your function that handles the data:
var div = "<div style=\"width:695px\" class=...........",
html = "";
for (var i = 0; i < data.length; i++) {
// Keep adding on to the same html string
html += div + data[i]._web._userName + "</div>";
}
// After the loop, replace the entire HTML contents of the container in one go:
$("#approvalContainer").html(html);
However, while this is fast, note that this is only appropriate if _userName doesn't contain special characters, or is already HTML escaped.