removeClass of Mootools is very slow on IE - javascript

I am applying addClass and removeClass on same set of elements in mooTools.addClass works fine but removeClass takes too long approx 6-7sec.Please see the code undeneath.
$('usermailbox').getElements('input[class=emailmessages]').each(function(el){
el.getParents().addClass('unChecked');//works fine
});
$('usermailbox').getElements('input[class=emailmessages]').each(function(el){
el.getParents().removeClass('selected'); //Takes too long in IE
});
Do I have any hope ?

Right. Several issues here... Obviously, w/o seeing the DOM it is a little difficult to determine but:
you do the same operations twice. in the same instance you can addClass and removeClass
doing element.getElements("input[class=emailmessages]") vs element.getElements("input.emailmessages") is probably slower but may return inputs that have other classes as well that you may not want.
el.getParents() will return all parents. you then iterate them again. twice. are you sure you don't mean just .getParent(), singular? if so, or if its one parent only, you are applying a .each on a single element, which is an unnecessary hit.
if your logic needs to remain then consider this as a single iteration:
store and walk.
var parents = el.getParents();
parents.each(function(p) {
p.addClass("unchecked").removeClass("selected");
});
all in all:
$("usermail").getElements("input.emailmessages").each(function(el) {
var parents = el.getParents();
parents.each(function(p) {
p.addClass("unchecked").removeClass("selected");
});
});
of course, with Slick / mootools 1.3 you can do it a lot more simple:
on this dom:
<div id="usermailbox">
<div class="selected">
<input class="emailmessages" />
</div>
<div class="selected">
<input class="emailmessages" />
</div>
<div class="selected">
<input class="emailmessages" />
</div>
</div>
the following will return all the divs:
// gets parents via a reverse combinator. you can do !> div or whatever to be more specific
// document.getElements("#usermailbox input.emailmessages !") will return all parents...
// ... in a single statement, inclusive of body, html etc... same as getParents
var divs = document.id("usermailbox").getElements("input.emailmessages !> ");
// single iteration per parent:
divs.each(function(div) {
div.removeClass("selected").addClass("unChecked");
});
of course, you can just do $("useremail").getElements("div.selected,div.unChecked") to get to these divs at any time anyway.... so it all depends on your DOM and needs, there must be a reason why you are doing what you are doing.
bottom line for perf:
cache results of lookups into vars. if you call $("someid") twice, cache it in your scope. if you do $("someid").getElements() twice, that's more than twice as bad in performance... and add .getParents() twice, thats ... n-times as bad now...
avoid applying chaqined methods to collections like this: collection.addClass("foo").removeClass("bar") - it will iterate it twice or n-times, go for a single .each callback instead, it will be much faster.
try to avoid reverse combinators or parents lookups if possible, go direct. you can use nth-child etc - other ways to walk your DOM than to get to the input and walk back. Especially when you don't really need the input itself...
.getParents(selector) will limit the types of parents you want. .getParents() will return buckloads, all the way up the parent / anchor dom node, often including the same ones for siblings.
always create a local scope with anonymous functions so you don't pollute your global object or upper scopes - IE does not like that and makes accessing variables slow.
Hope some of this makes sense, at least :) good luck and remember, speed is only relative and is as good as your performance in the slowest browser you support.

Related

Javascript/Jquery stable nested sibling

On an HTML page which repeats a nested structure like
<div>
<div class="ugga">
<button class="theButton">
</div>
</div>
several times, with one ".theButton" also having class "active", I would like to use jquery to find the button after the active button.
$(".theButton .active").parents(".ugga").parent().next().find(".theButton")
would roughly do the trick. However, this is still under development, so that I am not sure that the nesting level div/div/button as well as the parent element with ".ugga" will be stable. So whenever there is a structure change on the HTML side, I would have to change the above jquery-magic accordingly.
What is stable is that there will be a list of ".theButton" elements at some nesting level and all on the same nesting level.
Is there a simple way in Jquery to find the next button after the active one even if the structure is changed to just div/button or to form/div/div/button and the ".ugga" I rely on currently disappears? Something like
$(".theButton active").nextOnSameLevel(".theButton")
There's no short and simple solution I know of, which would let you to do that.
The most convenient way would be to have the HTML ready before setting up javascript for DOM manipulation. Even thinking of project updates I would personally spent that little while to change a small part of js.
However, if that's needed for some reason, then I would probably loop through the elements, to find the one I need, eg.:
var found = false;
$(".theButton").each(function(){
if(found){
// do something with $(this) ...
return false;
}
if($(this).hasClass('active')){
found = true;
}
});
JSFiddle
And yet another, oneliner solution:
$(".theButton").eq(($.inArray($(".theButton.active")[0], $(".theButton").toArray()))+1);
JSFiddle
This is for looking at all the other elements having the same parent as your element of interest.
$(".theButton active").siblings(".theButton");
This will return all the elements having theButton before and after your active button elements but if you're looking specifically for the element after active, use next() with a selector like this
$(".theButton active").next(".theButton");

Avoiding zIndex by appending dom elements

So I'm trying to control the stacking of equally-sized elements via z-index.
Now an idea that I came across recently to avoid going through z-indices and improve performance times by hopefully to avoiding browser reflows is instead order layers via the order I append things to the parent.
So if I have a container div that holds all the stacking divs, and linked list that mirrors the order, referencing the stacking divs, then I reorder the divs based on user input. Then instead of updating the z-indices, I would recreate the div element and just append everything in order. So something like this:
var from = nodeBeforeFrom; // Input
var target = nodeBeforeTarget; // Input
var linkedlist = input; // var linkedlist contains all the stacking divs
linkedlist.moveElement(div1, div2); //Move div1 to after div2
var container = document.createElement('div');
linkedlist.reorder; //
var cur = linkedlist.first;
while (cur.next) {
container.appendChild(cur)
cur = cur.next;
}
document.removeChild(oldContainer);
document.appendChild(container);
// This is meant as pseudocode so forgive an errors in regards to the specifics
So my questions are the following:
Would this reduce browser reflows from n reflows to just 1 or 2 (where n is the number of divs)? If I understand it right, changing the z-index of a single element should cause either a browser repaint or a reflow.
Will the second approach work and stack elements in the order you append them?
Is there a way to move childs around using the DOM's child node structure already so I don't have to create a separate linked list? I only see removeChild and appendChild functions that I can use at the moment.
And yes performance is an issue since I'm planning on using this for graphics and html5 stuff. So where I can save I would like to save.
Well great it seems I've answered my own question after playing around with things and some research thanks to the good people at Opera. Pretty much yeah it's faster to perform updates on hidden/unseen elements on the browser, then to add it to the DOM tree. I got my confirmation from here. The actual trick is to set the hidden CSS tag, perform all the operations that affect display, then set hidden back to true and that reduces your browser reflows from O(n) to just 2 total.
Also this method for avoiding z-index certainly works. I unfortunately still haven't found a way to access the childNodes linked list for DOM elements. However, taking a closer look at the specification, it turns out that childNodes for DOM nodes is read-only, which likely means it's not possible unless there's some vague hack around it.

id/class based selector v/s forms based selectors

Given the following HTML:
<fieldset>
<legend>
<input type="checkbox" name="amazon_activated" />
</legend>
<table>
<tr>
<td>
Amazon Data
</td>
</tr>
</table>
</fieldset>
next code allows hide/show the data container table related to the checkbox:
$("input[name='amazon_activated']").click(function(){
$(this).parent("legend").next("table").toggle( $(this).is(":checked") );
});
and next code it should init the hide/show state after page's load:
if ( ! $("input[name='amazon_activated']").is(":checked")){
$(this).parent("legend").next("table").hide();
}
Well, it is failing.
I already know why: this refers to the page element, not the checkbox element.
So I wonder:
Is it the best policy immediately choose an id/class:
$("#id")
for the important elements in order to facilitate their control through jquery, over form-based selectors?:
$("input[name='amazon_activated']")
Cache the element.
var element = this;
When this is in the right context.
You usually code Javascript for a given markup, not the other way around. ID's and classes have meaning other than being used as selectors.
I think you can cache your elements at the top of the outer function. It will be convenient to use them in other places. Also, caching elements will also enhance the performance.
You can do something like this:
var input = $("input[type='checkbox']"),
table = $("fieldset table");
if ( ! $("input[name='amazon_activated']").is(":checked")){
$(this).parent("legend").next("table").hide();
}
within above code this belong to page element scope, where
$("input[name='amazon_activated']").click(function(){
$(this).parent("legend").next("table").toggle( $(this).is(":checked") );
});
in above code this belongs to input[name='amazon_activated']'s callback scope which point to input[name='amazon_activated'].
So to make active first code you should try
if ( ! $("input[name='amazon_activated']").is(":checked")){
$("input[name='amazon_activated']").parent("legend").next("table").hide();
}
Its better to keep reference of input in a variable and use that
var checkboxElement = $("input[name='amazon_activated']"); // you can also use id
then you this like
if ( ! checkboxElement.is(":checked")){
checkboxElement.parent("legend").next("table").hide();
}
checkboxElement.click(function(){
$(this).parent("legend").next("table").toggle( $(this).is(":checked") );
});
I think the reason every answer is telling you to cache your objects is that it will be tremendously faster. To answer the specific question, disregarding the rest, i.e.:
Is it the best policy immediately choose an id/class for the important
elements in order to facilitate their control through jquery, over
form-based selectors?
First, I would say "attribute selectors" over "form-based selectors" as I don't believe jQuery distinguishes between, say $('input[name="amazon_activated"]') and $('a[href="#"]') as far as how it searches.
That said, the general rule of thumb is:
id selectors are faster than class selectors are faster than attribute
selectors.
So, if all you care about is jQuery speed, that's key. However, adding ids and classes, only for the sake of targeting via jQuery could slow down your page load times more than the corresponding speed-up in selector performance.
In summary to this overly-long answer:
Cache the result of a jQuery selector when possible
Use ids and classes when possible, but
Don't add unnecessary ids and classes unless testing proves them necessary
Thanks to all for your answers. All have been very helpful (I give +1 to all).
I want to add my final solutions that takes in account your tips.
Indeed there are several checkbox elements involved, so I have cached them and use an associative array to iterate over it (avoiding add more id/class).
var checkboxElements = {
"amazon_activated" : $("input[name='amazon_activated']"),
"clickbank_activated" : $("input[name='clickbank_activated']"),
"ebay_activated" : $("input[name='ebay_activated']")
}
$.each(checkboxElements, function(i, el){
$(el).parent("legend").next("table").toggle( $(el).is(":checked") );
$(el).click(function(){
$(el).parent("legend").next("table").toggle( $(el).is(":checked") );
});
});

Event delegation vs direct binding when adding complex elements to a page

I have some markup like this (classes are just for explication):
<ol id="root" class="sortable">
<li>
<header class="show-after-collapse">Top-Line Info</header>
<section class="hide-after-collapse">
<ol class="sortable-connected">
<li>
<header class="show-after-collapse">Top-Line Info</header>
<section class="hide-after-collapse">
<div>Content A</div>
</section>
</li>
</ol>
</section>
</li>
<li>
<header/>
<section class="hide-after-collapse">
<ol class="sortable-connected">
<li>
<header/>
<section class="hide-after-collapse">
<div>Content B</div>
</section>
</li>
</ol>
</section>
</li>
</ol>
That is, nested sortable lists. The sortable plugin suffices, however, since each li (hereafter "item") maintains its level, though the inner lists are connected. The items have an always-visible header and a section visible when in expanded state, toggled by clicking the header. The user can add and remove items from either level at will; adding a top-level item will include an empty nest list inside it. My question is with respect to JS initialization of the newly created item: While they will share some common functionality, which I can cover via
$("#root").on("click", "li > header", function() {
$(this).parent().toggleClass("collapsed");
});
and
li.collapsed section {
display: none;
}
(Side question: would this be an appropriate place to use the details/summary HTML5 tags? It seems sort of iffy about whether those will even make it into the final spec, and I want a sliding transition, so it seems like I'd need JS for that anyway. But I throw the question to the masses. Hello, masses.)
If the root list is the only (relevant) element guaranteed to be in existence at page load, for .on() to work effectively, I have to bind all the events to that element and spell out the precise selector for each, as I understand it. So, for example, to tie separate functions to two buttons right next to each other, I'd have to spell out the selector in full each time, à la
$("#root").on("change", "li > section button.b1", function() {
b1Function();
}).on("change", "li > section button.b2", function() {
b2Function();
});
Is that accurate? That being the case, does it make more sense to forgo .on() and bind my events at the time the new item is added to the page? The total number of items will probably number in the dozens at most, if that makes a difference in the response.
You will create less CPU overhead in binding the events using $(<root-element>).on(<event>, <selector>) since you will be binding to a single "root" element instead of potentially many more single descendant elements (each bind takes time...).
That being said, you will incur more CPU overhead when the actual events occur as they have to bubble up the DOM to the "root" element.
Short-story: delegate saves CPU when binding event handlers; bind saves CPU when events trigger (e.g. a user clicks something).
So it's up to you to decide what point is more important for performance. Do you have available CPU when you add the new elements? If so then binding directly to the new elements would be the best for overall performance however if adding the elements is a CPU intensive operation you will probably want to delegate the event binding and let the event triggering create some extra CPU overhead from all the bubbling.
Note that:
$(<root-element>).on(<event>, <selector>, <event-handler>)
is the same as:
$(<root-element>).delegate(<selector>, <event>, <event-handler>)
and that:
$(<selector>).on(<event>, <event-handler>)
is the same as:
$(<selector>).bind(<event>, <event-handler>)
.on() is new in jQuery 1.7 and if you are using 1.7+ then .delegate(<selector>, <event>, <event-handler>) is just a short-cut for .on(<event>, <selector>, <event-handler>).
UPDATE
Here is a performance test showing that it is faster to delegate event binding than to bind to each element individually: http://jsperf.com/bind-vs-click/29. Sadly this performance test has been removed.
UPDATE
Here is a performance test showing that event triggering is faster when you bind directly to elements rather than delegate the binding: http://jsperf.com/jquery-delegate-vs-bind-triggering (Note that this isn't a perfect performance test since the binding methods are included in the test, but since delegate runs faster on binding it just means that bind is even faster relatively when talking about triggering)
Since the accepted answer has inaccurate tests (BTW: test your code, measure performance, don't just blindly follow some "rules" - this is not how optimization is done!) and is simply wrong I post fixed tests:
https://jsperf.com/jquery-delegate-vs-bind-triggering/49
which proves on such simple example there is NO difference between delegation or direct binding
The only cases when delegation is always bad are events like mouse move and scroll - which are triggered x times per second. THIS is where you will notice any performance difference.
If you have even 1ms difference (won't happen, but this is just an example) on single event - like click - you won't notice that. If you have 1ms difference on event that happens 100 times in a second - you will notice CPU consumption.
Just having thousand of elements won't negatively impact your performance with delegation - actually - this is the case when they should be used - to avoid hogging CPU when attaching thousand of event handlers.
So if you really need a rule to follow (don't do that) - use delegation on everything except mouse move, scroll and other events that you can expect to fire continuously.

Performance of jQuery selectors

HTML markup:
<div>
<a id="foo"> </a>
</div>
jQuery:
$('div').each(function(){
$('#foo', this).dosmth(); // 1
$('#foo').dosmth(); // 2
});
Which method would be faster to run dosmth?
Since we're getting a variety of answers, hopefully here's some clarity (check the examples here):
The fastest - There's no need to loop. Skip the $("div").each part and just do $("#foo"). foo is an ID, and thus lookup is instantaneous.
Middling - $("#foo") in a loop. Note that you also don't want this because it will execute the function for every div on the page (and for this reason on a larger document with a lot of divs this would be the slowest).
Slowest - $("#foo", this). The context node doesn't help in the first place, and then consider that jQuery will first build a jQuery object out of this and turn it into $(this).find("#foo"). That's all unnecessary, of course.
Bottom line: in most cases (e.g. sometimes when confirming that an ID is in one context and not another) context nodes are unnecessary with ID lookup.
Here are some resources from the jQuery source:
Handling for most of the cases here - note that $("#id") is singled out for handling as document.getElementById
find - what happens when you pass a context
Since an #id should be unique in the DOM your markup will be invalid (I am assuming more than one <div/> based upon using .each())
Change the id to a class and use the following:
<div>
<a class="foo"> </a>
</div>
<div>
<a class="foo"> </a>
</div>
And the script
$('div').each(function(){
$('.foo', this).dosmth(); //or $(this).find(".foo");
});
But if you only have one element with an id of foo selecting by id will be the fastest, plus you can drop the need for using .each()
$('#foo').dosmth(); //or document.getElementById("foo");
jquery selectors by id only is the fastest way to search because it uses getElementbyId in javascript.
so this one is the fastest:
$('#foo').dosmth();
if you use a context like:
$('#foo', this).dosmth();
it is translated into:
$(this).find('#foo').dosmth();
so that will make another useless operation because your #foo is unique
Regards,
Max
$('#foo', this).dosmth();
This will search within the context of the div and not the whole DOM, which will make the selector faster. This only makes sense to use when the DOM is large, otherwise, just use the normal selector: $('#foo').dosmth();
If you're using an id there's only ever going to be one. So you can just do:
$('a#foo').dosmth();
You don't need to use each() to go through each div and get all the a#foo's out of it. That WILL waste time, creating loops for no reason. Instead use:
$('a#foo').each(function(){ ... });
or even just:
$('a#foo').dosmth();
You can also do $('div a#foo').dosmth(); if you want.
Please read the discussion below regarding this answer or check Bryan answer above about the differences in speed of the selectors.
I would go with
$('a#foo', this).dosmth();
Update But instead of retrieving all the divs before, I would check for
only the desired one at the first time
like this
$('div a#foo').each(function(){
}

Categories