Are tracking scripts slowing down your site?

Google Analytics, Facebook pixels, advertisement, Hotjar… Add tracking scripts or pixels is a common practice in most of the web sites.

But event the site respond at the same time with the HTML it takes longer to get it ready in the browser. Why?

Looking into the page rendering process

First, the browser gets the HTML, it scraps all CSS, parses it, generates the layout and prints it. Also, if it finds JS it stops till it’s executed. You can find an in-detail explanation in Constructing the Object model, from Google developers.

So, what happens when you add a typical tracking JS:

(function(h,o,t,j,a,r){
 h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
 h._hjSettings={hjid:944168,hjsv:6};
 a=o.getElementsByTagName(‘head’)[0];
 r=o.createElement(‘script’);r.async=1;
 r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
 a.appendChild(r);
 })(window,document,’https://static.hotjar.com/c/hotjar-’,’.js?sv=');

Even minified, we can see that this code is creating a <script> tag and adding it into the head. This modifies the parsed HTML, so the process of rendering the page starts again.

The final effect of adding some of this scripts is a long Time To Interactive:

Image for post
Note the 4seconds between the First Content Time and Time To Interactive. This values can vary depending on the device where the page is rendered.

We can see the detail in the Performance tab of the browser DevTools:

Image for post

In the main thread there is a ‘Parse HTML’ task after it already has been parsed. In the summary tab, in the bottom zone, the initiator is index, so the main HTML.

How to fix it

The most obvious solution is to get rid of this killer scripts. Let’s assume that is not an option.

So, if we should execute all these tasks in the browser the question is: when to execute these scripts?

The better solution is to execute it when the main thread is idle. This way we don’t delay the render either the interactive times.

For doing so we can use window.requestIdleCallback . This method appends tasks when the main process is done with the page. So we can do:

function idleWrapper() {
 (function(h,o,t,j,a,r){
 h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
 h._hjSettings={hjid:944168,hjsv:6};
 a=o.getElementsByTagName(‘head’)[0];
 r=o.createElement(‘script’);r.async=1;
 r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
 a.appendChild(r);
 })(window,document,’https://static.hotjar.com/c/hotjar-’,’.js?sv=’);
}
window.requestIdleCallback(idleWrapper);

Unfortunately requestIdleCallback is not fully supported yet. So we have to use a polyfill. Also, it’s possible that the idle time takes too long for us, so we can set up a timeout when it will be executed even is not idle:

window.requestIdleCallback = window.requestIdleCallback ||
 function (cb) {
 return setTimeout(function () {
 var start = Date.now();
 cb({ 
 didTimeout: false,
 timeRemaining: function () {
 return Math.max(0, 50 — (Date.now() — start));
 }
 });
 }, 1);
}
function idleWrapper() {
 (function(h,o,t,j,a,r){
 h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
 h._hjSettings={hjid:944168,hjsv:6};
 a=o.getElementsByTagName(‘head’)[0];
 r=o.createElement(‘script’);r.async=1;
 r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
 a.appendChild(r);
 })(window,document,’https://static.hotjar.com/c/hotjar-’,’.js?sv=’);
}
window.requestIdleCallback(idleWrapper, {timeout: 10000});

Originally published in: https://medium.com/quiron/are-your-tracking-scripts-slowing-down-your-site-78db0aa1c5e1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: