jQuery Performance Pitfalls
At FooLab, we have been using jQuery because it’s easy to learn, there is a big community with a lot of examples and advice and also because it’s easier to find developers familiar with it. It’s not a bad framework, but it’s very easy to abuse when you do not know how it works and what it implies.
Consider these two selectors: “.photo” and “#photo”. The first one will force jQuery to go through every single DOM element, searching for the class attribute, searching for the one containing the word “photo”, because you can have multiple class names separated with spaces. Now imagine that you do that for a dozen selectors, inside a function that can be executed multiple times per second. The second selector will simply go and get the element by id, directly. No traversing.
You can’t always use ids, but you can help jQuery narrow the scope. You can define a scope using a second parameter: $(“.photo”, container). In this example, container = $(“.container”) so jQuery has no business traversing elements outside of that container. If the container has an id, then you can just write $(“#container .photo”).
You can also prevent jQuery from traversing every element by specifying the node name $(“div.photo”). This way, jQuery will not have to check the class attribute if it’s not a div. Yes, it is more flexible to use the same class across various HTML tags, but you have to make a choice.
Sometimes you will win a mere 0.5%, and sometimes you will win 2,485% performance increase and be really happy that you read about selectors. These tips can also be applied to CSS selectors, to style your page faster.
When you change the size of an element in the middle of the page, the browser has to recalculate many (if not all) element sizes to render your page correctly. And we’re not just talking about height x width. Borders, padding, margins, line-height all have to be taken into account. We don’t think much of it when the page first loads, because we attribute it to the network latency, but rendering a page is actually a lot of work. If you ever move elements around dynamically and notice a delay, that’s how much time it takes… with all your CPU cores.
Say you are using the jQuery UI Button to skin your elements. The page is loaded with default button elements. All is calculated and positioned. Then for every single .button() you call, the size of that element changes, and the entire page is redrawn. So for an initialization function calling 20 .button(), you get 20 page redraws. You may even get that problem when you enable and disable the buttons dynamically. If you’re only using .button() for skinning, it’s better to let go and use CSS instead.
This one is related to the above measuring issue. When you .hide() an element, jQuery sets the display:none style, the space occupied by the element becomes empty and the page has to be redrawn. Whenever you can, set visibility:hidden instead. I once got a 2,740% performance increase on a commonly used function, with just that little thing.
Read my previous article to learn how to find the areas of your application that are eating the most CPU.