Ok to call document.querySelector( ) a bunch - javascript

If I have multiple instances of the following lines of code through out my js file:
document.querySelector('#IdName').play();
document.querySelector('#IdName').pause();
Is it a good idea to create a function and pass it the IdName(IdName will change in various parts of the code)? I know what it does but I'm really just curious if it's a good practice to call document.querySelector( )a bunch of times in the file or put it in a function where I only call it twice to perform the play and pause actions.

If you constantly need the same element, change the function to take a DOM node, and store the element in a variable instead
function doStuff(elem) {
elem.play();
}
function stopStuff(elem) {
elem.pause();
}
var element = document.querySelector('#IdName');
doStuff( element );
// later
stopStuff( element );
That way you only get the element once, and avoid unneccesary DOM lookups

The best approach is to cache that query in a variable so you don't need to search the DOM each time.
For an ID selector this time saving is likely minimal but for more complex collections can help
var $el = document.querySelector('#IdName');
$el.play();
$el.pause();

It is good practice to write code that is reusable, so in that case a function is better practice. If the function only contains 1 line of code and you call it many times, it is still preferable because then if you ever decide to update that line of code or add more code, it's centralized and you change in one place only.
As far as actual execution is concerned, these are the same:
document.querySelector('#IdName1').play();
document.querySelector('#IdName1').pause();
document.querySelector('#IdName2').play();
document.querySelector('#IdName2').pause();
document.querySelector('#IdName3').play();
document.querySelector('#IdName3').pause();
vs
playpause("#IdName1");
playpause("#IdName2");
playpause("#IdName3");
function playpause(idname){
document.querySelector(idname).play();
document.querySelector(idname).pause();
}

In addition to Steve's answer, also note that if you are using the same one twice in a row:
document.querySelector('#IdName').play();
document.querySelector('#IdName').pause();
then it is a better practice to do:
var thing_with_play_and_pause = document.querySelector('#IdName');
thing_with_play_and_pause.play();
thing_with_play_and_pause.pause();
This reduces the number of queries you have to make. Some IDEs (PyCharm for instance) will complain if you don't because it is less efficient.

Related

Create a unique function name on the fly for shared timer

I'll start with the exact nature of the problem and then give some background. I am trying to name a function -threadTimer- and give it a random unique identifier, such as 'threadTimer'+ID. A randomly generated ID would work fine. Then, I need to use setInterval on it, to make it fire repeatedly and therein lies my coding problem. I have tried every variation of new, function, function as an object and I just can't get my head around it. You'll notice that the function I have created is an object and perhaps this is where I'm going in circles.
OK, the background I mentioned. threadTimer is fired by a master timer co-ordinating several threads. That's why you'll see I have generated a 'global' object for reference elsewhere. similar HTML entities can fire threadTimer at the same time, hence my requirement to make each instance unique.
window['GlblThreadExe'+ID]=setInterval(function(){threadTimer(elid,parent,lft,top,diameter,point,bStyle,color,grp,startTime,size,ID,counter,div,divwth,divht,wthIncrement,htIncrement,lftStart,topStart,lftIncrement,topIncrement)},interval);
function threadTimer(elid,parent,lft,top,diameter,point,bStyle,color,grp,startTime,size,ID,counter,div,divwth,divht,wthIncrement,htIncrement,lftStart,topStart,lftIncrement,topIncrement){
// more code
}
In truth, I think its the volume of parameters that I'm passing that's confusing my syntax. Any help appreciated
Avoid polluting window
Generally instead of polluting the global namespace you can store your setInterval ids in some variable
let intervalIds = {}
intervalIds['GlblThreadExe'+ID] = setInterval(function()...)
If really necessary, then store intervalIds to window
window.intervalIds = intervalIds;
Wrap your anonymous function
When you create the "clock", do not call setInterval directly:
Here, createTimerWithId will return a function which calls threadTimer
Dirty id generation
Use a timestamp, and mix it with some random stuff. Or better use a UUID
setInterval(createTimerWithId(), 1000)
function createTimerWithId(){
let id = Date.now()+Math.random(); //no lib, oneliner. good enough to debug
return function(){//the same function you gave to setInterval in your example
threadTimer(id, ...)
}
}
We can do better
In 1. we generated an id on the fly and thus
your code is not testable (id will always change(well except if you mock Math and Date...)).
your id is ugly (a float...)
it will be hard to know from which setInterval you come from
instead, give it the ID.
function createTimerWithId(ID){
return function(){//the same function you gave to setInterval in your example
threadTimer(ID, ...)
}
}
window['..'+ID] = setInterval(createTimerWithId(ID));
shorter version being
window['..'+ID] = setInterval((id=>{
return function(){
threadTimer(id, ...)
}
})(ID),1000);

Searching Up the Scope vs Accessing the DOM - Speed Performance

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

How should I use Variables and jQuery Dom navigation?

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.

Why is "this" more effective than a saved selector?

I was doing this test case to see how much using the this selector speeds up a process. While doing it, I decided to try out pre-saved element variables as well, assuming they would be even faster. Using an element variable saved before the test appears to be the slowest, quite to my confusion. I though only having to "find" the element once would immensely speed up the process. Why is this not the case?
Here are my tests from fastest to slowest, in case anyone can't load it:
1
$("#bar").click(function(){
$(this).width($(this).width()+100);
});
$("#bar").trigger( "click" );
2
$("#bar").click(function(){
$("#bar").width($("#bar").width()+100);
});
$("#bar").trigger( "click" );
3
var bar = $("#bar");
bar.click(function(){
bar.width(bar.width()+100);
});
bar.trigger( "click" );
4
par.click(function(){
par.width(par.width()+100);
});
par.trigger( "click" );
I'd have assumed the order would go 4, 3, 1, 2 in order of which one has to use the selector to "find" the variable more often.
UPDATE: I have a theory, though I'd like someone to verify this if possible. I'm guessing that on click, it has to reference the variable, instead of just the element, which slows it down.
Fixed test case: http://jsperf.com/this-vs-thatjames/10
TL;DR: Number of click handlers executed in each test grows because the element is not reset between tests.
The biggest problem with testing for micro-optimizations is that you have to be very very careful with what you're testing. There are many cases where the testing code interferes with what you're testing. Here is an example from Vyacheslav Egorov of a test that "proves" multiplication is almost instantaneous in JavaScript because the testing loop is removed entirely by the JavaScript compiler:
// I am using Benchmark.js API as if I would run it in the d8.
Benchmark.prototype.setup = function() {
function multiply(x,y) {
return x*y;
}
};
var suite = new Benchmark.Suite;
suite.add('multiply', function() {
var a = Math.round(Math.random()*100),
b = Math.round(Math.random()*100);
for(var i = 0; i < 10000; i++) {
multiply(a,b);
}
})
Since you're already aware there is something counter-intuitive going on, you should pay extra care.
First of all, you're not testing selectors there. Your testing code is doing: zero or more selectors, depending on the test, a function creation (which in some cases is a closure, others it is not), assignment as the click handler and triggering of the jQuery event system.
Also, the element you're testing on is changing between tests. It's obvious that the width in one test is more than the width in the test before. That isn't the biggest problem though. The problem is that the element in one test has X click handlers associated. The element in the next test has X+1 click handlers.
So when you trigger the click handlers for the last test, you also trigger the click handlers associated in all the tests before, making it much slower than tests made earlier.
I fixed the jsPerf, but keep in mind that it still doesn't test just the selector performance. Still, the most important factor that skewes the results is eliminated.
Note: There are some slides and a video about doing good performance testing with jsPerf, focused on common pitfalls that you should avoid. Main ideas:
don't define functions in the tests, do it in the setup/preparation phase
keep the test code as simple as possible
compare things that do the same thing or be upfront about it
test what you intend to test, not the setup code
isolate the tests, reset the state after/before each test
no randomness. mock it if you need it
be aware of browser optimizations (dead code removal, etc)
You don't really test the performance between the different techniques.
If you look at the output of the console for this modified test:
http://jsperf.com/this-vs-thatjames/8
You will see how many event listeners are attached to the #bar object.
And you will see that they are not removed at the beginning for each test.
So the following tests will always become slower as the previous ones because the trigger function has to call all the previous callbacks.
Some of this increase in slowness is because the object reference is already found in memory, so the compiler doesn't have to go looking in memory for the variable
$("#bar").click(function(){
$(this).width($(this).width()+100); // Only has to check the function call
}); // each time, not search the whole memory
as opposed to
var bar = $("#bar");
...
bar.click(function(){
bar.width(bar.width()+100); // Has to search the memory to find it
}); // each time it is used
As zerkms said, dereferencing (having to look up the memory reference as I describe above) has some but little effect on the performance
Thus the main source of slowness in difference for the tests you have performed is the fact that the DOM is not reset between each function call. In actuality, a saved selector performs just about as fast as this
Looks like the performance results you're getting has nothing to do with the code. If you look at these edited tests, you can see that having the same code in two of the tests (first and last) yield totally different results.
I don't know, but if I had to guess I would say it is due to concurrency and multithreading.
When you do $(...) you call the jQuery constructor and create a new object that gets stored in the memory. However, when you reference to an existing variable you do not create a new object (duh).
Although I have no source to quote I believe that every javascript event gets called in its own thread so events don't interfere with eachother. By this logic the compiler would have to get a lock on the variable in order to use it, which might take time.
Once again, I am not sure. Very interesting test btw!

jQuery: is element.click(func) or element.attr('onclick','func()') more efficient?

I'm populating a list by cloning elements into it. Then I change attrs to make each item unique. They need to call a function on click, so I'm wondering if it's more efficient to use new_element.click(func); or new_element.attr('onlick','func();');
new_element.attr('onclick','func();');
Is:
inefficient (needlessly creating a new inline function from a string, that does nothing except call func and lose the this reference);
aggravating to put any complex code in, since it all has to be JS string escaped;
broken in IE, due to bugs in setAttribute.
Avoid. click()/bind('click') is there for a reason.
onclick has a number of limitations, including cluttering the DOM and only allowing one function at a time. So you should use click. See Quirks Mode for more information.
Directly referencing the function will be more efficient than having to interpret a string.
The lowest touch way of doing this, however, is this way:
$(links_selector).live('click', func);
links_selector will presumably be something like ul.listClass a.actionClass. This will not require anything to be done when new list elements get added.
Since you are using jQuery then make it this way
new_element.click(function(){
// your code
});
or you can bind the click event handler like
new_element.bind("click", function(){
// your code
});
Any difference in performance between the two is most likely going to be negligible. You should use the one that reads better, and that's element.click. (Plus, onclick has many disadvantages, as #Matthew Flaschen mentioned.)

Categories

Resources