What is the most efficient way to access DOM elements in jQuery? - javascript

I am writing a jQuery plugin and I am at the optimization stage.
I wonder which of these leads to a quicker script in general, and what sort of mitigating factors are important:
Have the script generate lots of classes and ids at the start so finding elements in the dom is easier (quicker too?) later on.
Keep classes and ids to a minimum (so save time by not creating them), but then look for elements in more convoluted ways later (eg the nth item in an object), which I presume is slower than getting by id/class.

The way the browser works is that upon load it creates an in-memory DOM tree which looks as follows:
P
_______________|______________
| |
childNodes attributes
______________|___________ |
| | | title = 'Test paragraph'
'Sample of text ' DIV 'in your document'
|
childNodes
__________|_______
| | |
'HTML you might' B 'have'
So when you lookup P > DIV > B, the lookup has to find all P elements, then find all DIV elements within P and then find all B elements within DIV. The deeper the nesting the more lookups it needs to do. Further, it might find all P > DIV only to find that none of them have B and it will have wasted time looking through all P > DIV matches.
Lookups by ID are faster because IDs are guaranteed to be unique so the DOM can store them as hash table entries which have very fast lookups. In terms of jQuery the implementation might be slightly different, however, document.getElementById has the fastest lookup time so $('#my_node_id') should also be quite fast.
Consequently, if your node doesn't have an ID, you can find the nearest ancestor that does and find your node relative to that ancestor
#my_node_id > div > b
because the look up only needs to happen under the sub-tree of #my_node_id it will be faster than p > div > b

The question is not really specific enough so I can give you advice directly relevant to your code, but here are some of my favorite jQuery optimization tips:
Whenever possible, specify a context! Don't make jQuery look places it doesn't have to. If you know that something is going to be inside the #container div, then do $(something, '#container');
.myclass is slow. Internally, jQuery has to go through every single element to see if it has the class you are searching for, at least for those browsers not supporting getElementsByClassName. If you know that a class will only be applied to a certain element, it is way faster to do tag.myclass, as jQuery can then use the native getElementsByTagName and only search through those.
Don't do complicated selectors in one bang. Sure, jQuery can figure out what you want, but it takes time to parse out the string and apply the logic you want to it. As such, I always like to separate my "queries" out into patches. While most people might do $('#myform input:eq(2)'); or something like that, I prefer to do $('input','#myform').eq(2); to help jQuery out.
Don't re-query the DOM if you plan on doing multiple things with a set of objects. Take advantange of chaining, and if not possible to use chaining for whatever reason, cache the results of the query into a variable and perform your calls on that.

I think you answered your own question: 'convoluted ways' is a synonym for 'it will break' - any changes to the html structure will break your code.
For example, imagine you're trying to get myDiv and you assume it's the last sibling of child:
<div>parent
<div>child</div>
<div>myDiv</div>
</div>
What happens if you later decide the structure should really be like this?
<div>parent
<div>child</div>
<div>myDiv</div>
<div>child</div>
</div>
By relying on assumptions about structure your code becomes really brittle. Adding classes and ids to nodes will prevent such scenarios.
I think you should go with the first choice. Do also remember that getting nodes by class is always slower than getting them by id.

With my plugins I try, as best I can, to limit the amount of hooks I introduce into the document. By "hooks" I mean IDs or classes.
The best way to avoid them completely is to retain references to any created elements within your plugin's closure. For example:
jQuery.fn.addList = function(items, fn){
var $list = $('<ul/>').html('<li>' + items.join('</li><li>') + '</li>');
return this.each(function(){
$(this).append($list);
fn.call($list);
});
};
$('body').addList(['item 1', 'item 2'], function(){
var referenceToTheList = this;
console.log(referenceToTheList, '<- this is my list, no hooks involved!');
});
The list can be carried around JavaScript functions (referenced and used in multiple places) without requiring any hooks in the HTML; thus making it as unobtrusive as possible!
Avoiding/limiting hooks is especially important in plugin development because you never know where it might end up being used.

Related

Click on "Confirm" button by getting the Button Text inside a span [duplicate]

I have a html code like
<div>
<span>TV</span>
</div>
I want to find this span through documentObject having text 'TV', like getElementById etc ... something like getElementByText. I know that it's possible through XPath/JQuery/Regex.
But I need it to get through DOM object model only. As only DOM model is available in my context.
I see couple of answers:
Finding an html element ID based on a text displayed
jquery - find element that only has text and not any other html tag
how to find element after some text with javascript?
But these are not helpful to me, as I need to get it through DOM model only.
Assuming the document is well-formed enough to parse into a proper DOM object tree, you can iterate through the entire structure without using an external library. Depending on the structure, you may have to examine every node to find all matches, and this may be slow. If you have access to IDs of any sort, you may be able to reduce search scope and improve performance.
The key property you will need is the childNodes collection on every DOM node. Starting with the BODY (or some other container), you can recurse through all the child nodes.
This site is pretty basic but shows dependency-free methods for accessing DOM elements. See the section called "Tools to Navigate to a Certain Element".
I noticed that you mentioned regular expressions as a means to find elements. Regexes are poor for parsing entire documents, but they can be very useful in evaluating the textual content of a single node (e.g. partial matches, pattern matches, case insensitivity, etc.) Regular expressions are part of the JavaScript language itself and have been so for well over a decade.
Only thing I can think of is something like this:
function getElementByTextContent(text)
{
var spanList = document.getElementsByTagName("span");
for (var i = 0, len = spanList.length; i < len; i++)
{
if(spanList[i].textContent === text) // use .innerHTML if you need IE compatibility
return spanList[i]
}
}
of course it assumes you are only searching for <span> elements, but this might work for you. Here's a demo as well:
http://jsfiddle.net/uATdG/

Global JQuery Selector caching to increase performance

I am trying to increase the jquery performance of my mobile html 5 app. I read some guides about storing used jquery selectors in global objects. The app is pretty big and I didn't expected a big perfomrance boost, but the app was running even slower (like 20%).
I only use jquery to find elements by Id ( $("#id") or $_element.find("#id")). Ids are unique, so I am only interested in finding the first element. I managed to globalize all jquery calls in a cacheHandler object, which stores all selectors by id. The cache is cleared frequently and contains around 30 elements per cycle.
With this changes the app ran slower, so I tried some more things to increase performance:
cache[id] = $(document.getElementById("id"))
cache[id] = document.getElementById("id")
store selectors with hashCode: cache[id.hashCode()]
I came up with the idea, that this solution is slow, because the memory is increased frequently, because the whole dom with all its children is stored in the cache.
So I had the new Idea, to cache the element path as array, like
document.body.children[1].children[5].children[0] => [1,5,0]
So I just have to find the element once, store the array and look up the path, if I need the element again.
This doesn't change anything, and all ideas were even slower than calling $("#id"), whenever I need the element.
If needed I can provide more informations or snippets.
I am thankfull for each explanation, why this is slowing down my app.
If it's a mobile html5 app why are you using jQuery for selectors? Seems very redundant.
I usually do somthing along the lines of this:
// helpers, since i hate typing document.get ..
function _id(e){ return document.getElementById(e); } // single id
function _all(e){ return document.querySelectorAll(e); } // single elem
function _get(e){ return document.querySelector(e); } // multiple elem
// loop helper (for changing or doing things to collection of elements)
function _for(e,f) { var i, len=e.length; for(i=0,l=len;i<l;i++){ f(e[i]); }}
// VARs (global)
var c = _id('c'), // main selector
box = c.querySelectorAll('.box'), // boxes in 'c'
elements = box.querySelectorAll('.element'); // elems in 'box'
// Change background-color for all .box using the _for -helper
_for(elements, function(e){ e.style.backgroundColor = 'red'; }
I only store main parents of elements so that i can then query down the DOM if needed (limiting the lockup needed for traversing). In the example variables above one could imagine that something in the .box would change several times OR that .box is a slow selector.
Do note that global variables increase memory usage though since those variables can interfer with garbage collection. Also note that objects can be slower in some browsers and if it doesn't bloat your code to much you should instead store it more plainly (you shouldn't store too many global variables anyway so....).
Edit, jsPerf:
http://jsperf.com/global-vs-local-vars-14mar2015
Do note however that depending on what you're selection and exactly what you're doing will have the greatest impact. In the jsPerf-example the difference between local and global quickly diminishes as soon as one starts selecting descendants from the globally cached selectors ie doing box.find('p').css('color','blue') etc.
This is quite old but we never know, someone can read this post.
jQuery is based on Sizzle which is way smaller: https://sizzlejs.com/
You can eventually include only this library. I would not recommend to maintain your own piece of code for this purpose. It is already done and maintained by someone else.

Restructuring DOM searching method?

As part of a Chrome extension I am searching the entire DOM for elements containing specific words inside each ID/Class.
Currently it looks something like:
"allSelectors": document.querySelectorAll("[id*='example'][class*='example']"),
"collapse": function () {
for (var i = 0; i < MyObject.allSelectors.length; i++) {
// Manipulate MyObject.allSelectors[i] etc
}
},
First, I would like to restructure it somehow (possibly using an array?) so that it is easy to add new selectors as doing it like this:
document.querySelectorAll("[id*='example'][class*='example'][id*='other'][class*='other']")
Isn't easily scaleable or good.
Secondly, I think document.querySelectorAll is very slow - the reason I am using it is because I need to search anywhere in an id/class (hence the use of *=) and cannot use a big external library (such as jQuery), as this is a small file and is being injected user side.
Is there an any solution to either of these problems? because if there are many matches then this slowness might become an issue.
First of all I would totally go for querySelectorAll, I don't think it's that slow, and also it totally fits in a situation like yours. I agree with you that adding a library is overkill for this, and additionally it might not be as beneficial as someone thinks (let's test it here).
Then, again I agree with you that the current solution is not very scalable and that the array is the way to go. Here's a very basic implementation using an array:
// an array of classes and ids to match
var nodes,
searches = [
'[id*="test"]',
'[class*="example"]'
];
// a simple function to return an array of nodes
// that match the content of the array
function getNodes(arr){
return Array.prototype.slice.call(document.querySelectorAll( arr.join() ));
}
nodes = getNodes(searches);
The good thing is that new classes and ids can be easily added or removed from the array, for example, later on you can add:
searches.push('[id*="some"]');
nodes = getNodes(searches); // new nodes will be fetched
Here's a jsbin with a full example code.

Which jQuery operation is faster for 20+ elements?

Code 1:
$("#page").replaceWith(html_string)
Code 2:
$("#page .title").text("Hello")
I am starting to use Mustache.js, jQuery.tmpl, and Coffeekup and am starting to wonder what best practices are in terms of re-rendering html elements.
Say I have a list of items, like here, and I want to just change the titles/tags/images with JSON via a templating framework. Should I:
Replace the whole list itself with a new html string?
Replace each list item with a new html string?
Replace just the text/attributes in every single element with the json values?
I know the last case will require traversing the dom a lot more, but I'm not sure if that's more of a performance hit than replacing the whole html tree. How does this work?
I tend to think that you shouldn't destroy DOM elements just to recreate them immediately.
The last case doesn't really add any DOM traversal, but it does modify the DOM several times instead of just once.
If you're concerned about performance you can always use the detach()[docs] method to remove the elements from the page, do your manipulation on its content, then append them.
// reference the container
var ul = $('#myul');
// select and detach the children
var lis = ul.children().detach();
// do your manipulation
lis.each(function() {
// update the text
});
// reinsert them
ul.append(lis);
The .detach() will keep the elements and all their handlers/data intact while you manipulate them.
A jQuerytmpl template is a compiled javascript function, which should be even faster then rendering html strings.
Imagine the browser. It reads first all the html and parses it then. With a jquery template you go one step further and provide the browser the already parsed object.
Compiled templates like jQuery.tmpl are the fastest way i think, faster than any html, if coded good.
Waiting for critics SO! :)
no different
for Code 1:
$("#page").replaceWith(html_string)
Code 2:
$("#page .title").text("Hello")

One tagname, multiple attrs - faster to stem queries with filter or use multiple selectors?

I'm wondering which of the following jQuery queries is going to be faster. I'm looking to match any span tags that have either an src attribute or a data-src attribute.
var a = $('span[src],span[data-src]');
var b = $('span').filter('[src],[data-src]');
My gut feeling is (b) but there may be optimisations I'm not aware of.
Thanks.
UPDATE:
Based on a quick test with 100 x span[src], 100 x span[data-src], and 100 x span elements: (a) came out around 4 - 8 times faster depending on the browser. On IE8 it was a lot faster (about 50 times) and on IE6/7 about the same.
What I'm wondering is why is it faster?
In case (a) jquery delegates the entire selector query the native querySelectorAll where available. So what looks like a slower query is actually very fast on modern browsers.
At http://jsfiddle.net/B9Vmy/1/ i ran some tests (use firebug for the console stuff)
for 6000 spans (2000 spans,2000 with src,2000, 2000 with data-src) this ran in about 8ms so the selection part is neglectable. What makes this so slow is the creation of the jQuery objects (jQuery(element)).
While A does that only once for only the returned results, B does it for every element, after that uses those results and does that again for the returned results.
And then there's the sizzle engine possibly being used for selecting the elements... Take a look at the Filter timing for a laugh :-)
It is just a hypothesis, more based on logic than test.
In first case, the selector will select the span with src attribute.
In second case, the selector will select all span and then filter span with attr src.
So, first will be faster.
If I am wrong, correct me
The first version will do two passes of the complete DOM tree; whereas the second one only once.
The real answer requires a profiling on your specific case i.e. depending on how complex your DOM is. Also, different jQuery version might result in different results.

Categories