I am learning javaScript and there are still many doubts. I've already tried to search this one but maybe I am using the wrong words. I am always delaying this but since the size of the project I am working on is becoming larger than I expected I need to be clarified.
I am aware that are costs in terms of speed in both searching up the scope and accessing the Dom. But I don't know which one is slower than the other.
Is it faster...
a) to go up the scope chain to grab the variable with the jQuery object, but go to the Dom just once
or
b) Not go up the scope chain, but go to the Dom again
var $el = $("#el");
//$el is used on this scope (and so, the question makes some sense:))
$el (...)
// some or lot's of code
function a() {
// some or lot's of code
function b(){
// some or lot's of code
function c() {
a) $el (...)
b) var $el = $("#el");
$el (...)
}
}
}
Option A is faster. (Declaring the variable on top)
When you need $("#el") more then once store it as a variable. Using the DOM multiple times is slow.
Greg Franko explains a few best practices here. See slide 10-13 for your question.
While your question is very unclear, perhaps I can answer it with a general rule of thumb:
The DOM is slowwwww. If you can avoid traversing it, do so. If you search the DOM to find a specfic node, store that reference in a variable to avoid having to search through the DOM again.
If I understanded your question, you are asking about performance accessing elements in jQuery. Here you have some tips:
Every time you have to get the reference to a jQuery object ($("#el") in your example) has a operational cost. In terms of performance, you should always try to declare the minimun necessary elements to your needs.
Accessing siblings (siblings()), parents (closest(), parent()) or children (children(), find()) is a good option if is just for a few operations, but if you are going to use that elements many times, its better reference them with a direct selector like $("#el-child") or ("#el .child").
If you declare a var like $el = $("#el") you get a reference of the node in its current state. Sometimes this node has being updated, removed, re-created, or something else, and this reference wont keep trak of them, so you have to call this $el = $("#el") again so, as I said before, it depends how your website logic works.
Note: sorry about my english, I know it's not good at all hehe
Related
I was just wondering which is the correct or most efficient way of navigating through the Dom using variables.
For example, can I concatenate selectors
var $container = '.my-container';
$($container).addClass('hidden');
$($container + ' .button').on('click', function(){
//something here
});
or should I use the jQuery traversal functions
var $container = $('.my-container');
$container.addClass('hidden');
$container.children('.button').on('click', function(){
//something here
});
Is there a different approach, is one best, or can you use them at different times?
The $ is usually used only when working with an actual jquery object. You generally shouldn't prefix anything with that unless it's really something from jquery.
Beyond that little bit though, performance-wise, your second bit of code is going to be faster. I made an example jsperf here: http://jsperf.com/test-jquery-select
The reason the second bit of code is faster is because (if I remember correctly) jquery caches the selection, and then any actions performed on that selection are scoped. When you use .find (which is really what you meant in your code, not .children), instead of trying to find elements through the entire document, it only tries to find them within the scope of whatever my-container is.
The time when you wouldn't want to use the second pattern is when you expect the dom to change frequently. Using a previous selection of items, while efficient, is potentially a problem if more buttons are added or removed. Granted, this isn't a problem if you're simply chaining up a few actions on an item, then discarding the selection anyway.
Besides all of that, who really wants to continuously type $(...). It's awkward.
I've seen some JavaScript code to access HTML elements like this: elementID.innerHTML, and it works, though practically every tutorial I searched for uses document.getElementById(). I don't even know if there's a term for the short addressing.
At first I thought simplistically that each id'ed HTML element was directly under window but using getParent() shows the tree structure is there, so it didn't matter that elements I wanted were nested. I wrote a short test case:
http://jsfiddle.net/hYzLu/
<div id="fruit">Mango<div id="color">red</div></div>
<div id="car">Chevy</div>
<div id="result" style="color: #A33"></div>
result.innerHTML = "I like my " + color.innerHTML + " " + car.innerHTML;
The "short" method looks like a nice shortcut, but I feel there is something wrong with it for it practically not appearing in tutorials.
Why is document.getElementById() preferred, or may be even required in some cases?
Why shouldn't I access elements more “directly” (elemId.innerHTML)
Because, according to the others in this thread, referencing arbitrarily by id name is not fully supported.
So, what I think you should be doing instead is store their selections into a var, and then reference the var.
Try instead
var color = document.getElementById('color');
color.innerHTML = 'something';
The reason why this would be a good thing to do is that performing a lookup in the DOM is an expensive process, memory wise. And so if you store the element's reference into a variable, it becomes static. Thus you're not performing a lookup each time you want to .doSomething() to it.
Please note that javascript libraries tend to add shim functions to increase general function support across browsers. which would be a benefit to using, for example, jquery's selectors over pure javascript. Though, if you are in fact worried about memory / performance, native JS usually wins speed tests. (jsperf.com is a good tool for measuring speed and doing comparisons.)
It's safer I guess. If you had a variable named result in the same context that you are doing result.HTML I'm pretty sure the browser will throw a wobbler. Doing it in the way of document.getElementById() in this instance would obviously provide you with the associated DOM element.
Also, if you are dynamically adding HTML to the page I may be wrong, but you could also encounter unexpected behaviour in terms of what result is :)
Also I will add that not all ID's can have values that will not work as variable names. For instance if your ID is "nav-menu".
Although I suppose you could write window["nav-menu"].innerHTML
Which makes me think, what happens if you create a window level variable with the same name as an ID?
Checkout this jsfiddle (tested in chrome): http://jsfiddle.net/8yH5y/
This really seems like a bad idea altogether. Just use document.getElementById("id") and store the result to a variable if you will be using the reference more than once.
I am building a MooTools class and I have this in my initialize function:
this.css = null;
window.addEvent('domready', function(){
this.document = $(document);
this.body = $(document.body);
this.head = $(document.head);
}.bind(this));
Ok and now to the questions ...
Should I declare this.css = null or any other empty variable in the init:
this.css = null; // Maybe this.css = '' - empty string?
Next thing is about window and document ... Should I put it into $() or not because it works both way, so I just want to know which way is preferred? So to summarize:
window.addEvent() // or should I use $(window).addEvent()
this.document = $(document) // or this.document = document
this.body = $(document.body) // or this.body = document.body
I stored these values into object to avoid multiple DOM queries, is this ok? Or would it be better to call $(selector) / $$(selector) every time?
Two more things left ... About binding ... Is it ok to use .bind(this) every time or would it be better to use .bind(this.myDiv) and use it inside function as eg.: this.setStyle(...); instead of this.myDiv.setStyle(...)
(function(){
this.setStyle('overflow-y', 'visible');
}.bind(this.body)).delay(100);
or
(function(){
this.body.setStyle('overflow-y', 'visible');
}.bind(this)).delay(100);
And the last thing is about garbage collection ... Do I have to garbage myself and how to do it (as far as I know MooTools does it on its own on unload). The confusing part is that I found function in MT docs:
myElement.destroy();
They say: Empties an Element of all its children, removes and garbages the Element. Useful to clear memory before the pageUnload.
So I have to garbage on my own? How to do that? When to use .destroy()? I am working on a huge project and I notice that IE gets slow over multiple executions of the script (so how to handle that? probably some cleaning needed, memory leaks?).
pff, this is a bit long.
first, initial variables. this.css = null... the only time i'd set 'empty' variables are: typecast; when it's a property of an object i may reference and don't want undefined; when it's a string i will concatenate with or a number i will incre/decrement; null is not really useful at this point.
a common / good practice when writing a mootools class is to use the Options class as a mixin. this allows you to set default options object with your default settings that can be overridden upon instantiation. similarly, Object.merge({ var: val}, useroptions); can override a default val if supplied.
now, iirc, there are times when you'd have to use $(document.body) and it's not because document.body does not work, it's because applying $() also applies Element prototypes in IE (since Element prototype is not extended there, the methods are applied to the elements directly instead, which happens when you $ them). Also, $ assigns an internal UID of the target element and allows for element storage to be used for that element. I don't see a point to using $(document) or $(window) - they are 'extended' as much as needed by default. In any case, even in IE, you only need to $(something) the one time and can continue using it as just 'something' afterwards. check my document.getElementById("foo").method() example - you can just run $("foo"); on it's own and then try document.getElementById("foo").method() again - it will work in IE too.
window.addEvent(); // is fine.
document.body.adopt(new Element("div")); // not fine in IE.
new Element("div").inject(document.body); // fine.
and on their own:
$(document.body).adopt(new Element("div")); // fine.
document.body.adopt(new Element("span")); // now fine, after first $.
see this in ie8: http://www.jsfiddle.net/AePzD/1/ - first attempt to set the background fails but the second one works. subsequently, document.body.methods() calls are going to work fine.
http://www.jsfiddle.net/AePzD/2/ - this shows how the element (which $ also returns) can have methods in webkit/mozilla and not in trident. however, replace that with $("foo") and it will start working. rule of thumb: $ elements you don't dynamically create before applying methods to them.
storing selectors can be a good performance practice, for sure. but it can also fill your scope chain with many variables so be careful. if you will use a selector two or more times, it's good to cache it. failing to do so is not a drama, selector engines like sizzle and slick are so fast these days it does not matter (unless you are animating at the time and it impacts your FPS).
as for binding, whichever way you like.
keep in mind delay has a second argument, BIND:
(function(){
this.setStyle('background', 'blue');
}).delay(100, $("foo"));
so do quite a few functions. this particular bind is not very useful but in a class, you may want to do
(function(){
this.element.setStyle('background', 'blue');
this.someMethod();
}).delay(100, this));
GC. mootools does it's own GC, sure. however, .destroy is a very good practice, imo. if you don't need something in the DOM, use element.dispose(). if you won't attach it to the DOM again, use .destroy() - removes all child nodes and cleans up. more memory \o/
advice on IE... dodgy. you can use drip if you can to trace memory leaks, there are things like dynatrace that can be very good in profiling. in terms of practices... make sure you don't use inline js, you always clean up what you don't need (events, elements) and generally, be careful, esp when you are stacking up events and dealing with ajax (bring new elements that need events - consider event delegation instead...). use fewer dom nodes - also helps...
$('#element').method();
or
var element = $('#element');
element.method();
Without using a profiler, everyone is just guessing. I would suspect that the difference is so small it isn't worth worrying about. There are small costs to the second above the first like having to preform a lookup to find 'var element' to call the method on, but I would have thought finding '#element' and then calling the method is far more expensive.
However, if you then went on to do something else with element, the second would be faster
//Bad:
$('#element').foo();
$('#element').bar();
//Good:
var e = $('#element');
e.foo();
e.bar();
If you were using a loop where the value of $('#element') was used a lot, then caching it as in the 2nd version before the loop would help a lot.
For just this small snippet, it makes little difference.
Lookups via id (#) are pretty fast. I just tested your scenario on a small page with 2 div tags. Here is the code i used
var x = $("#div1");
var y = $("#div2");
var z = $("#div1");
every lookup took about 0.3ms on my laptop. The 2nd lookup for div1 executed the same internal jQuery methods as the first - indicating that there is no caching of already looked up objects
Performance becomes a bigger problem when you use other selectors like classname or more advanced jQuery selectors. I did some analysis on jQuery Selector Performance - check it out - hope it is helpful.
If you run only this code, no one should realy be faster. The second one might need more memory (because of the additional variable created).
If you want to be sure, why not test it yourself using a small selfwritten benchmark?
I think $('#element').method(); does not need as much memory as
var element = $('#element');
... because you bind #element to a variable.
Juste fore funne
\Indifferent:
$('#element').foo().bar();
We're considering switching our site from Prototype to jQuery. Being all-too-familiar with Prototype, I'm well aware of the things about Prototype that I find limiting or annoying.
My question for jQuery users is: After working with jQuery for a while, what do you find frustrating? Are there things about jQuery that make you think about switching (back) to Prototype?
I think the only that gets me is that when I do a selection query for a single element I have to remember that it returns an array of elements even though I know there is only one. Normally, this doesn't make any difference unless you want to interact with the element directly instead of through jQuery methods.
Probably the only real issue I've ever ran into is $(this) scope problems. For example, if you're doing a nested for loop over elements and sub elements using the built in JQuery .each() function, what does $(this) refer to? In that case it refers to the inner-most scope, as it should be, but its not always expected.
The simple solution is to just cache $(this) to a variable before drilling further into a chain:
$("li").each(function() {
// cache this
var list_item = $(this);
// get all child a tags
list_item.find("a").each(function() {
// scope of this now relates to a tags
$(this).hide("slow");
});
});
My two pain points have been the bracket hell, can get very confusing
$('.myDiv').append($('<ul />').append($('<li />').text('content')));
My other common issue has to do with the use of JSON in jQuery, I always miss the last comma,
$('.myDiv').tabs({ option1:true, options2:false(, woops)});
Finally, I've been using jQuery for about 6 months now and I don't think I'll ever go back to prototypes. I absolutely love jQuery, and a lot of the tricks they use have helped me learn a lot. one cool trick that I like is using string literals for method calls, I never really did that too much with prototypes.
$('.myDiv')[(add ? 'add' : 'remove') + 'Class']('redText');
(The only thing I can think of is that this is the element instead of a jQuery object in $("...").each(function)-calls, as $(element) is more often used then just the element. And that extremly minor thing is just about it.
Example of the above (simplified and I know that there are other much better ways to do this, I just couldn't think of a better example now):
// Make all divs that has foo=bar pink.
$("div").each(function(){
if($(this).attr("foo") == "bar"){
$(this).css("background", "pink");
}
});
each is a function that takes a function as parameter, that function is called once for each matching element. In the function passed, this refers to the actual browser DOM-element, but I find that you often will want to use some jQuery function on each element, thus having to use $(this). If this had been set to what $(this) is, you'd get shorter code, and you could still access the DOM element object using this.get(0). Now I see the reason for things being as they are, namely that writing $(this) instead of this, is hardly that cumbersome, and in case you can do what you want to do with the DOM element the way it is is faster than the way it could have been, and the other way wouldn't be faster in the case you want $(this).)
I don't think there are any real gotchas, or even any lingering annoyances. The other answers here seem to confirm this - issues are caused simply by the slightly different API and different JavaScript coding style that jQuery encourages.
I started using Prototype a couple of years ago and found it a revelation. So powerful, so elegant. After a few months I tried out jQuery and discovered what power and elegance really are. I don't remember any annoyances. Now I am back working on a project using Prototype and it feels like a step back (to be fair, we're using Prototype 1.5.1).
If you reversed the question - "What Prototype annoyances should I be aware of as a jQuery user?" - you would get a lot more answers.
Nope. Nada. Nyet.
.each:
jQuery (you need Index, even if you're not using it):
$.each(collection, function(index, item) {
item.hide();
});
Prototype (you're usually using the item, so you can omit the index):
collection.each(function(item) {
item.hide();
});
This is really only an annoyance if you're doing a lot of DOM manipulation. PrototypeJs automatically adds its API to DOM Elements, so this works in prototypejs (jQuery of course doesn't do this):
var el = document.createElement("div");
el.addClassName("hello"); // addClassName is a prototypejs method implemented on the native HTMLElement
Even without running the native element through the $() function.
PS: Should note that this doesn't work in IE.