How to minimize performance hit from cookie consent code?

178 Views Asked by At

Here is very basic html that I created to test the impact of cookie consent code.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="description" content="test page speed">   
  <title>Speed test 1</title>
  <meta content="width=device-width, initial-scale=1" name="viewport">
  <link href="favicon.png" rel="shortcut icon" type="image/x-icon">
  
    <!-- BEGIN Cookie Consent -->
    <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.css"/>
    <script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js"></script>
    <script>
    window.addEventListener("load", function(){
    window.cookieconsent.initialise({
      "palette": {
        "popup": {
          "background": "#eee",
          "text": "#000"
        },
        "button": {
          "background": "#0f0",
          "text": "#ffffff"
        }
      }
    })});
    </script>
    <!-- END Cookie Consent -->

</head>

<body class="body">
<h1>Hello!</h1>

<p>
This is a test.
</p>

</body>
</html>

Without the Cookie Consent section, the FCP is 0.8s (as measured by Google's PageSpeed Insights).

With the Cookie Consent section, the FCP is 1.9s. A huge drop in performance!

What can be done to get back to FCP around 1s?

2

There are 2 best solutions below

3
VonC On BEST ANSWER

To minimize the performance impact of your cookie consent code, you might consider loading your Cookie Consent script asynchronously, using the HTML <script> async Attribute.

<script async src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js"></script>

You could try and minimize/combine external resources. Unfortunately, without access to the source of your cookie consent library, you cannot directly minimize or combine these resources. But you can make sure you are using the minified versions, as you already are. Consider hosting these files yourself if you need further optimization.

You can also defer the initialization of the cookie consent until after the DOMContentLoaded event. That makes sure the initialization code does not block the rendering of the page.

<script>
document.addEventListener('DOMContentLoaded', function () {
    if(window.cookieconsent) {
    window.cookieconsent.initialise({
        "palette": {
        "popup": {
            "background": "#eee",
            "text": "#000"
        },
        "button": {
            "background": "#0f0",
            "text": "#ffffff"
        }
        }
    });
    }
});
</script>

That would also illustrate a good practice when initializing the cookie consent: by checking if window.cookieconsent is available (as in this example), you avoid potential errors and make sure the script only runs when necessary.


Could you please explain why you recommend using "DOMContentLoaded" and not "load"?"

The "DOMContentLoaded" event is fired when the HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading. That means your script will run as soon as the HTML is fully parsed, but potentially before all external resources (like images or stylesheets) have loaded.
By using this event, you make sure your cookie consent code runs earlier in the page load process, which can be advantageous because it does not have to wait for all resources to load. That can lead to a quicker interactive state for the page, improving user experience, especially on slower connections or devices.

The "load" event, on the other hand, waits for the complete page to load, including all dependent resources like stylesheets and images. That event is fired much later in the page lifecycle.
If you initialize the cookie consent script on the "load" event, the script will only execute after everything on the page has fully loaded, which can delay the display and functioning of the cookie consent popup. That delay might not be ideal, especially if user interaction with the consent is required before they can fully interact with the page.


Salman A adds in the comments:

async + calling window.cookieconsent.initialise inside domcontentready means the function might not get called at all (there is high chance of that since domcontentready does not wait for async scripts to load)

That is possible. To make sure the Cookie Consent script is available when you attempt to initialize it, you can implement a more robust check that waits for both the document to be ready and the script to be loaded. That can be done using a combination of event listeners and checks for the script's loading state:

<script>
// That function will attempt to initialize the cookie consent once
function initializeCookieConsent() {
  if (window.cookieconsent) {
    window.cookieconsent.initialise({
      "palette": {
        "popup": {
          "background": "#eee",
          "text": "#000"
        },
        "button": {
          "background": "#0f0",
          "text": "#ffffff"
        }
      }
    });
  }
}

// Event listener for DOMContentLoaded to make sure HTML is fully parsed
document.addEventListener('DOMContentLoaded', initializeCookieConsent);

// Load the cookie consent script asynchronously
var script = document.createElement('script');
script.onload = initializeCookieConsent; // Attempt initialization on script load
script.async = true;
script.src = "//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js";
document.head.appendChild(script);
</script>

By dynamically creating a script element and setting its src, you make sure the script is loaded asynchronously. The onload event handler attached to the script ensures initializeCookieConsent is called once the script is available, addressing the issue Salman highlighted.
The DOMContentLoaded listener also attempts to initialize the cookie consent. If the script loads quickly enough (before the DOM is fully loaded), the initialization will occur once the DOM is ready. If the script loads after, the script's onload handler takes care of initialization.

The original <script async src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js"></script> line would be replaced by the dynamic script loading approach detailed in the solution. That method involves creating a <script> element programmatically in JavaScript and setting its src attribute to the URL of the Cookie Consent script.

That dynamic loading method gives you more control over when and how the script is loaded. By attaching an onload event handler to the script element, you can make sure the initialization code for the cookie consent is executed immediately after the script has loaded, regardless of whether the DOM has finished loading.

0
Salman A On

I would rather run PageSpeed Insights on a finished product and start fixing problems severity-wise.

That being said, I see only one fixable issue:

Eliminate render-blocking resources

To fix this:

  1. Inline the contents of the CSS file
  2. Load the JavaScript file asynchronously

Unfortunately, it looks like the chosen library will not work asynchronously (the documentation seems to indicate that you need to load the script before you can configure it, and I could not find an asynchronous-friendly[1] method). You can use this old trick as a workaround:

<!--
  before you proceed, inline the contents of cookieconsent.css
  into a <style> tag (copy necessary styles only)
  then place the following script tag anywhere you like
-->
<script>
  (function() {
    let s = document.createElement("script");
    s.src = "//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js";
    s.onload = function() {
      window.cookieconsent.initialise({
        "palette": {
          "popup": { "background": "#eee", "text": "#000" },
          "button": { "background": "#0f0", "text": "#fff" }
        }
      });
    };
    document.head.appendChild(s);
  })();
</script>

This should get rid of the "Eliminate render-blocking resources" warning:

No more warnings


[1] Scripts designed for asynchronous loading are usually included and configured like so:

<div
  class="someplugin-target"
  data-someplugin-clientid="1234"
  data-someplugin-textcolor="blue"
></div>
<script>
  window.somePlugin_ExtraConfig = {
    backgroundColor: "white"
  };
</script>
<script
  src="//cdn.com/someplugin.js"
  async
></script>

I would suggest you ask the author of the plugin to add this feature.