Improving performance re-using selected elements (Become an expert)
In this recipe, we'll see how we can store a collection of previously selected elements in a variable for a later processing and how this method can improve performance.
How to do it...
This time our goal will be a little different. Instead of applying margins or borders, we'll perform a comparison to show how in a (simulation of a) real-world page, caching objects can significantly improve performance. In order to understand the explanation, follow these steps:
Create a copy of the
template.html
file and rename it asreusing-elements.html
.Edit the
<head>
section of the page adding this code:<script> $(document).ready(function() { var start = new Date(); var $description = $('.description'); for (i = 0; i < 50000; i++) { $description.text(); } console.log('Time with caching: ' + (new Date() - start) + ' milliseconds'); start = new Date(); for (i = 0; i < 50000; i++) { $('.description').text(); } console.log('Time without caching: ' + (new Date() - start) + ' milliseconds'); }); </script>
Save the file and open it with your favorite browser.
How it works...
Basically, our code can be split into two blocks. Their general goal is to retrieve all of the elements with class "description" and then call the jQuery's text()
method a given number of times (50,000 in our example). The difference is that while the first retrieves the collection once, stores it into a variable and then calls text()
inside the loop, the second calls the constructor inside the loop as well. Of course, it's unlikely that you'll call a constructor so many times inside your code, but the demo wants to stress on the performance difference that a simple caching can make.
Before each loop, we save the current time in milliseconds using the JavaScript Date
class in a variable called start
. Then, right after the loop, we print the elapsed time on the console. The difference between the two blocks is quietly impressive, and is shown by the following table:
Test #1 |
Test #2 |
Test #3 |
Average | |
---|---|---|---|---|
Time with caching |
18 ms |
17 ms |
17 ms |
17,3 ms |
Time without caching |
227 ms (more than 209 ms) Approximately 11, 5x slower |
216 ms (more than 199 ms) Approximately 11x slower |
224 ms (more than 207 ms) Approxiamtely 11, 5x slower |
222,3 ms (more than 205 ms) Approxiamtely 11, 5x slower |
Note
While a rough benchmark can be done using the Date
class, a more reliable one should be performed with the High Resolution Time API. You can find more information on this API reading this article of mine at http://www.sitepoint.com/discovering-the-high-resolution-time-api/.
From this recipe, we can learn a good rule of thumb: if we're going to use a selector at least twice, we should cache it. Then, when you need to call it again, you'll just use the variable where you stored the collection. Doing so, jQuery won't need to search the entire DOM tree again to find the elements, which leads to better performance.
Some of you may argue that the example shown couldn't be seen as a real use case. For those of you, the following is a piece of code taken directly from the current version of my website. The aim of this snippet is to rotate my latest tweets, but this doesn't really matter. What I want you to focus is the way I cached the collection of elements having class tweet
inside the variable called $tweets
and then reused it many times, especially in the anonymous function:
// Rotate the latest tweets var $tweets = $('.tweet'); $tweets.not(':first-child').hide(); window.setInterval( function () { $tweets .filter(':visible') .fadeOut('slow', 'linear', function () { var $next = $(this).next($tweets.selector); if ($next.length === 0) { $next = $tweets.first(); } $next.fadeIn('slow', 'linear'); }); }, 5000 );