Blocking and long running script tags
When scripts get downloaded and then executed by the browser the browser typically stops all other downloads. This behaviour can easily be observed looking at the network breakdown view as in the following illustration:
The dynaTrace AJAX Edition Timeline View shows exactly what happened during these periods:
The next interesting view is the PurePath view. It shows the full execution trace of every individual script tag (and also all other script event handlers). By double clicking on a script execution block in the TimeLine view or by drilling from a method identified in the HotSpot view we get to the PurePath view showing the exact trace:
Slow CSS Selectors with jQuery/Prototype
The #1 problem we have seen since we released dynaTrace AJAX Edition is the use of class name based CSS Selectors used with frameworks like jQuery or Prototype.
What are CSS Selectors
The query $(“div.vt_tabPanel”) returns all DIV tags that have the CSS Class vt_tabPanel assigned. jQuery needs to use the underlying capabilities of the browser to find the elements that match this criteria. Different browser provide different types of query methods. Internet Explorer for instance does not provide a method to look up elements by class name. Other browser provide the method getElementsByClassName. As this method is missing in IE jQuery simulates this functionality by iterating through the whole DOM and checking every single DOM element whether it matches the class name of not. Depending on the size of the DOM this can become a very lengthy operation.
How to improve CSS Selector performance
There are several ways to improve CSS Selector performance especially on Internet Explorer but also on other browsers.
Use Unique ID when possible
The fastest way to lookup a single element is to use its unique id. If your query is only returning a single element you should give this element a unique id and then use the lookup by ID.
Instead of $(“div.vt_tabPanel”) we can use $(“#myTabPanel”). On average looking up elements by ID is about 95% faster than looking up elements by class name.
Specify a Tag name if you have to use the Class Name
When looking up elements by class name and these elements are all of the same tag type, e.g: DIV, then use the tag name as part of the query.
Instead of $(“.vrtc_activetabdiv”) we can use $(“div.vrtc_activetabdiv”). The latest versions of jQuery is smarter when resolving these queries than some older ones. If you specify a tag name jQuery will first resolve these elements using getElementsByTag (which is natively supported by all browsers). It then iterates through all these elements and matches the class name. This approach saves a lot of execution time as jQuery doesn’t need to iterate through ALL DOM Elements but only those of a specific tag.
Specify a parent context
By default jQuery queries the whole DOM to return the requested elements specified by the selector query. It also allows you to pass in a context object as second parameter. This context parameter causes jQuery to only query the elements underneath the passed context object instead the whole document. If you are looking for certain objects and they all share a common parent you are better off passing the common parent as context object.
Instead of $(“div.vt_tabPanel”) we could use $(“div.vt_tabPanel”, $(“#tabArea”)). This approach saves a lot of execution time as jQuery doesn’t need to iterate through ALL DOM Elements but only through the child elements of the parent context object.
Cache Lookup Results
As we have learned – lookups can be very expensive. Therefore we should avoid unnecessary lookups. Results of lookups can be saved in a variable and be reused at a later stage. A perfect example is loops where the same object is looked up all over again.
Instead of this code:
you better do
Reduce the DOM Size
Too many XHR calls
A mistake that is often made is that too much information is fetched dynamically with too many calls. One example is a product page with 10 products. The developer may decide to use AJAX to load detailed product information for every product individually. This means 10 XHR calls for every 10 products that are displayed. This will of course work but it means that you have 10 roundtrips to the server that lets the user wait for the final result. The server needs to handle 10 additional requests that puts additional pressure on the server infrastructure.
Instead of making 10 individual requests it is recommended to combine these calls into a single batch call requesting the product details for all 10 products on the page.
Manipulating the DOM
Manipulating the DOM can be very expensive. Changing the class name – especially on the body tag causes the browser to re-evaluate all elements on the page.
Performance Savings, Recommendations and Rank Calculation
Recommendations and Savings
Script blocks that execute longer than 20ms are considered to have potential for improvement. The longer a script block executes the more impact it has on the overall performance and therefore results in a lower Rank. We take the overall execution time of blocks that execute longer than 20ms. Every 50ms reduces the Page Rank by 1 point.
We also take XHR calls into the Rank calculation. We consider more than 5 XHR calls as too many and therefore penalize the page for more than 5 calls. Pages that we have analyzed either show a handful of calls during the load phase. On pages where there was a problem we usually see far more than 5 calls which is often a result of incorrect implementation and not followed best practices (too chatty).
Note: On interactive sites this approach will lower the Page Rank as XHR is often used in mouse and keyboard event handlers to download additional content. Therefore this Rank might be misleading on pages with heavy interaction.
We end up with a Page Rank of 45 which corresponds to an F Grade.
Here are further reads and detailed explanation on Cache Settings
- dynaTrace Best Practices on Web Site KPIs, Browser Caching, Network Resources and Server-Side Activities
- jQuery Performance Rules and jQuery Blog