Why is this Javascript loop taking one minute for 100 iterations? - javascript

I am using the below code in my program but it seems that these few line of code is taking too much time to execute. For 100 iteration it is consuming 1 mins approx. for 200+ iteration my broser is showing a warning message that script is taking too much time. As per the scenario 500+ ids can be pushed into the array.
for (var i = 0; i < arrid.length; i++)
{
$("#" + outerDiv + "> div[id=" + arr[i] + "]").attr("class", "selected");
}
arrid is an array of div ids. Outerdiv is the the parent div of all these div ids present in arrid. arr ids cannot be accessed directly, it has to be referenced using the parent div i.e. outerDiv.

One quick thing you could do is cache your selector so jQuery does not have to query the dom 500+ times.
var $div = $("#" + outerDiv);
for (var i = 0; i < arrid.length; i++)
{
$div.children("div[id=" + arr[i] + "]").attr("class", "selected");
}
On second thought, since you have a list of id's, you shouldn't need any of that as the id should be unique per the dom.
for (var i = 0; i < arrid.length; i++)
{
$("#" + arr[i]).attr("class", "selected");;
}

If outerDiv is an element, you can write
for (var i = 0; i < arrid.length; i++)
{
$("#" +arr[i], outerDiv).attr("class", "selected");
}
But assuming the id's are unique, you shouldn't need to reference the outer div at all. It might even be faster not to.
Also, if this is all you're doing and you're concerned about performance, why not just use plain ol' javascript?
for (var i = 0; i < arrid.length; i++)
{
document.getElementById(arr[i]).className = "selected";
}

Using jQuery function is expensive - as is manipulating the DOM.
You can reduce to one call to jQuery like this:
var divSelector = [];
for (var i = 0; i < arrid.length; i++)
{ // add selector to array
divSelector.push( "#" + outerDiv + "> div[id=" + arr[i] + "]" );
}
// execute jquery once with many selectors
$(divSelector.join(',')).attr("class", "selected");
This code is untested, but should work in principal.

Don't call jQuery selection function too many times
Instead select your elements once and do the rest in a different way. For this thing to work it would be better to convert your arr array of IDs into an accosiative array that can make searching much much faster.
// convert arr = ["idOne", "idTwo", ...]
// into an associative array/object
// a = { idOne: true, idTwo: true, ... }
var a = {};
$.each(arr, function(index, el){
a[el] = true;
})
// do the rest
$("#" + outerDiv " > div[id]").each(function(){
if (a[this.id] === true)
{
$(this).addClass("selected");
}
})
The most inner call can be as well replaced with:
this.className = "selected"
when you can be sure no other classes will be added to the element.
But when you want to set selected on all child div elements (if your IDs cover all elements) then a simple:
$("#" + outerDiv " > div[id]").addClass("selected");
would do the trick just fine.

Store the instance into a variable($div) and use another variable to store the max length the loop for will do.
var $div = $("#" + outerDiv);
for (var i = 0, max = arrid.length; i < max; i++)
{
$div.children("div[id=" + arr[i] + "]").attr("class", "selected");
}

How about this as you have the id's of all the elements you are trying to change the class on:
for (var i = 0; i < arrid.length; i++)
{
$("#" + arr[i]).addClass("selected");
}
I know using the selector div[id=val] performs very slow is certain browser based on a speed test I ran, worth a look as it is pretty interesting:
http://mootools.net/slickspeed/

I think you can directly push jQuery object to the array, then you can just arr[i].attr('class', 'selected')

Related

jQuery Browser gets freeze when to add mass rows into div

When I need to add up about 2000 elements by jQuery function append(), browser gets freeze, and seems everything stuck for some time long.
Is this because of jQuery it'self issue or.. any logical issue on my coding side?
What I am doing is like following:
for (var i=0; data.length; i++) {
add_elem(data[i]);
}
function add_elem(data) {
$("#wrapper").append('<div class="row" id="elem_' + data['id'] + '>' + data['htmlv'] + '</div>');
uniform_elem($('#elem_' + data['id']));
}
uniform_elem() has some ui processing to make things look good.
Any good idea?
Instead of calling add_elem in the loop, store all of the html for the elements in a single variable as you loop through the data variable and then invoke add_elem by passing in the new variable.
var elements = "";
// Store all elements in the loop
for (var i=0; data.length; i++) {
elements+= '<div class="row" id="elem_' + data['id'] + '>' + data['htmlv'] + '</div>';
}
// Invoke add_elem once and only once using the new elements var
add_elem(elements);
function add_elem(data) {
// Append once to the DOM
$("#wrapper").append(data);
for (var j=0; data.length; j++) {
uniform_elem($('#elem_' + data['id']));
}
}
If you want to work with each object, you may do:
for (var i=0;i< data.length; i++) {
let u=i;
setTimeout(()=>{
add_elem(data[u]);
},0);
}
This pushes the calls onto the browsers qeue, wich is just executed when the main thread is free ( so no blocking)

For cycle or for each with js objects

I got a problem to add a onclick event to object who can be many times in same page
I am trying to
var i;
for (i = 1; i<=10; i++) {
var tmpObj='lov_DgId_D_'+i;
var tmpObj2=tmpObj.getElementsByTagName('a')[0];
if (tmpObj2 != null) {
tmpObj2.onclick= DgIdOnClick;
}
}
But got a error TypeError:
Object lov_DgId_D_1 has no method 'getElementsByTagName' , but this is working
lov_DgId_D_2.getElementsByTagName('a')[0].onclick= DgIdOnClick;
This ibject lov_DgId_D_ can be from 1 like lov_DgId_D_1 or lov_DgId_D_99 u.t.c
What wil be the best solution to add onclick to all lov_DgId_D_* objects ?
As you use jquery, the simplest is
for (i = 1; i<=10; i++) {
$('#lov_DgId_D_'+ i+ ' a').click(DgIdOnClick);
}
If you want to bind your event handler to all a elements inside elements whose id starts with lov_DgId_D_, then it's as simple as
$('[id^="lov_DgId_D_"] a').click(DgIdOnClick);
The problem you have is a confusion between the id of an element and the actual element. Some code that should work for you is this one:
var i;
for (i = 1; i<=10; i++) {
var tmpObj=document.getElementById('lov_DgId_D_'+i); // <-- here
var tmpObj2=tmpObj.getElementsByTagName('a')[0];
if (tmpObj2 != null) {
tmpObj2.onclick= DgIdOnClick;
}
}
Slightly easier to read code:
for (var i=0; i<=10; i++) {
var anchor = document.querySelector('#lov_DgId_D_'+i + ' a');
if (anchor) anchor.onclick = DgIdOnClick;
}
A note: this code attaches a click event to the first anchor (a element) inside each element with the id lov_DgId_D_n, with n being 1->10. Your original code seems to want to do the same thing.
Another note: usually when you iterate over elements using their id's to identify them, you are better suited to add a class to those elements instead. It provides for more maintaintable code and probably easier to understand as well.

Shorthand for loop passing three times even if array lenght is 1?

I have this javascript function in external .js file:
function init() {
var v = document.getElementsByTagName('video'),i;
console.log(v.length);
for (i in v) {
console.log("class:" + v[i].className + "id:" + v[i].id);
}
}
init();
And one video element in dedicated html page. This is what script returns to Chrome console:
1 // v.length
class:video1id:bigBunny //first pass of for loop
class:undefinedid:undefined //??
class:undefinedid:undefined //??
Why is this happening?
A NodeList (returned by getElementsByTagName) has not only the elements but two additional properties:
length (the amount of elements)
item (to get an element, basically the same as using [i] notation)
You're iterating them as well and treating as if they are elements. They're not; they don't have a class nor an ID. You should use a numeric for loop (for(var i = 0; i < v.length; i++) instead. This (unlike for in) obviously can't include such properties.
You should really be using a traditional for loop anyway.
for(var i = 0; i < v.length; i++)
{
console.log("class:" + v[i].className + "id:" + v[i].id);
}

How to refer to specific jquery ui tabs by their index such as in an array

One thought is that perhaps I should create an array and just .each() all of the tabs to create references in the manner I would like but it seems there is probably already a way to do this I just may not be aware of the scope or syntax.
For instance I have $tabs.tabs() and I want to change the AJAX URL and title of a given index such as:
for (var x = index; x < (tab_counter - 1); x++) {
$tabs.tabs(x).text(!!$tabs.tabs(x + 1).text() ? $tabs.tabs(x + 1).text() : "Deleted");
//is there a syntax to refer to tabs in this manner?
}
$tabs has been initialized as
var $tabs = $("#pages").tabs( ...
Assume that tab_counter is kept up to date (it is incremented and decremented in the add and remove events) - If I try as suggested something like
for (var x = 0; x < (tab_counter - 1); x++) { console.log($tabs.tabs.eq(x).text() + " # index " + x);
} In order to for example loop through all of the tabs titles. Is there something I am missing here in the syntax? eq() seems to be the right solution, but I suspect I am not using it right.
Solution
$tabis = $('#pages ul li a');
for (var x = 0; x < tab_counter; x++) {
console.log($tabis.eq(x).text() + " at " + x);
}
/* Prints the text label of each tab within the selected list of tabs. */
Thank you for your help dbaseman thats exactly what I was looking for. And I learned in the process so I am greatful for that
Use eq perhaps?
$tabs.eq(x).text( ... )
The tabs are all given consistently-named id attributes you could select.
One solution is to make you own function that returns objects from the IDs of each tab:
$tabs.tabs = function (index) {
return $("#tab" + index);
}
This assumes you tabs have IDs of "tab0", "tab1", etc., but it could work with any IDs.

Replace element HTML faster in jQuery

I am populating a list with about 25,000 items, using code like this:
var html = "";
for(var i = 0; i < reallyLongArray.length; i++) {
html += "<li><a href='#'>Hi</a></li>";
}
$("#list ol").html(html);
Somewhat to my surprise, I used a profiler and found out that the bottleneck in my code was not the loop that iterated thousands of times, but setting the html of the list to the string. This usually takes about 5-10 seconds on my computer, which is an order of magnitude too slow.
Is there a way to do this that is significantly faster, i.e., at least 10 times faster?
Wrap the html in a single item. When jQuery builds elements from a string, it adds all top level items by iterating them. If you wrap the list items in a single element, it should go much faster because it only has to add 1 top level element to the dom.
var html = "<ul>";
// your loop...
var html += "</ul>";
// add list html to list container
Aside from directly using innerHTML:
$("#list ol").get(0).innerHTML = html;
...and trying the "stringbuffer" technique:
var html = [];
for(i = 0; i < reallyLongArray.length; i++) {
html.push("<li><a href='#'>Hi</a></li>");
}
$("#list ol").html(html.join(''));
...not really.
Using DOM methods to create it should work faster:
var list = ("#list ol");
for(i = 0; i < reallyLongArray.length; i++) {
$(document.createElement('li'))
.append($(document.createElement('a'))
.text('Hi')
.attr({href: 'foobar'})
)
.appendTo(list);
}
edit: Actually, using DocumentFragment should make this even faster:
var fragment = document.createDocumentFragment();
for(i = 0; i < reallyLongArray.length; i++) {
fragment.appendChild($(document.createElement('li'))
.append($(document.createElement('a'))
.text('Hi')
.attr({href: 'foobar'})
)
.get(0)
);
}
$('list ol').append(fragment);
You might also want to clear() the <ol> before adding the elements to it
another edit I've created a jsperf test at http://jsperf.com/insert-html-vs-dom-manipulation - both those versions are slower than setting the innerHTML (because jQuery is used to create the elements). Using dom maniuplation with native methods is much faster than setting the HTML, BUT the fastest way, by a large margin, is using DOM manipulation with DocumentFragment without jQuery, using native methods.
Array joins are faster than string manipulation.
var html[];
for(i = 0; i < reallyLongArray.length; i++) {
html.push(string);
}
$(selector).html(html.join(''));
This will speed it up by quite a bit. String concatenation can take a long time when used a lot because of what happens "under the hood".
$(document).ready(function(){
var html = new Array();
for(i = 0; i < reallyLongArray.length; i++) {
html[i] = "<li><a href='#'>Hi</a></li>";
}
document.getElementById('list').getElementsByTagName('ol')[0].innerHTML = html.join("");
});
One thing to just note here, is that when doing an iteration of 25,000, it will be difficult to reduce the time down to milliseconds if you are inserting a large number of records. This happens especially in IE as it parses each new element before inserting it. Building a "pager" and caching the items you are going to insert will significantly speed that up.
$.grep(ComboBoxData, function (e) {
if (e.Text.toLowerCase().indexOf(TextBox) != -1 || e.ID.toLowerCase().indexOf(TextBox) != -1) {
Show = true;
Result += "<li hdnTextID='" + hdTextID + "' onclick='Select(this," + TextBoxID + ")' onmouseover='triggerTheli(this)' HdntriggerID='" + e.HdntriggerID + "' postBack='" + e.TriggerPostBack + "' Event='" + e.EventName + "' HdnID='" + e.hdnID + "' SelectedValue='" + e.ID + "' style='background:" + Color + "'><a>" + e.Text + "<p> " + e.Description + " </p></a></li>";
if (Color == "#eff3ec")
Color = "#fff";
else
Color = "#eff3ec";
}
});
a good example of mine

Categories