Implementing JavaScript Delay for Cookie Consent Banner


Over some time I have been looking for ideal Cookie Consent Banner implementation on my websites. The main goal was to make sure it’s not causing CLS (Cumulative Layout Shift) resulting in poor Core Web Vitals (and downgrading website in Google Search).

I found the solution that worked for me for some time.

Together with other optimisation works I managed to achieve my goal, however, from time to time I saw a spike in PageSpeed Insight for some pages and I couldn’t figure out what else I could do to make sure that Cookie Banner (Bar) is not causing CLS.

I know, that Cookie consent banners are utter nonsense as I said on my website, however, there are places (commercial application) when going into court for breaching such nonsense like EU Cookie Law can cost a lot. In that case, you like it or not, you need to use it.

On one day I looked into a source code of Smashing Magazine (my number one website for web development news). They made a lot of optimisation works on their end and they understand “a fight” with web metrics and poor core Web Vitals firsthand from their case study.

I admire their persistence in that matter and so I looked into their implementation of the cookie banner. I found that they are using, new to me, the approach of delaying (do not mix with deferring) of JavaScripts until “actual” user interaction with their website (and not when the site is finishing loading). This approach is used not only for what I have been looking for but also for other scripts.

Because my cookie bar solution is a good base to start, I decided to go a step forward and implement their approach to improve it even further and here’s how it goes.

Before I start, I will recall my solution.

My cookie bar is a combination of HTML file with simple code, JavaScript with the execution and CSS for styling.

Let’s start with HTML

<div id="CookieBanner" class="cookie-banner js-cookie-banner">
    This site use 🍪cookies. Read more in <a href="/cookies-policy/">Cookies Policy</a>.
  <button class="cookie-btn">Ok!</button>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="cookie.js" async></script>

Depending on how your website is built, you may put it all in the footer, incorporate style with yours. As I am using Hugo, I am calling it through partial. As I will not cover Hugo here, I will just work on the basics.

Adding some basic styling to it:

#CookieBanner { display: none; }

.cookie-banner {
  position: fixed;
  right: 0;
  bottom: 0;
  min-width: 100%;
  background: white;
  color: black;
  text-align: center;

.cookie-banner p {
	font-size: 1rem;
	padding: 1rem 0.5rem 0.375rem 0.5rem;

.cookie-banner a {
	color: black;

.cookie-btn {
	background: red;
	border: 0;
	color: white;
	padding: 0.375rem 2rem;
	font-size: 1rem;
	margin-bottom: 1rem;
	border-radius: 0.5rem;
	cursor: pointer;

Of course, you will modify this and adjust to responsive layout using @media query. Possibilities are limitless but don’t overdo it, especially with fonts. Keep it simple and stick to system font stack. It’s just a cookie banner, not a decoration for your website.

As you will see, by default, the banned is hidden by display: none;. Here is a little magic that happens with JavaScript.

if (localStorage.getItem("cookieBannerDisplayed")) {
} else {
  document.getElementById("CookieBanner").style.display = "block";

function dismiss() {
      localStorage.setItem("cookieBannerDisplayed", "true");

  const buttonElement = document.querySelector('.cookie-btn');
  if (buttonElement) {
      buttonElement.addEventListener('click', dismiss);

In short.

When you visit my (other) website and you never clicked on the cookie banner, you will be presented with it. That is the part where style is changing from display: none; to display: block.

When you click Ok button (.cookie-btn), your answer will be stored in localStorge of your browser. Next time you will visit that website, the cookie banner will be hidden straight away as there will be an element in your browser localStorage called cookieBannerDisplayed with the value true.

The above code is available on my Github > idarek/cookies-bar.

As you notice in the beginning, JavaScript is called via the following part:

<script src="cookie.js" async></script>

Using the above part, the JavaScript will be loaded together with other elements of the website. Sometimes this will interfere with CLS, but unlikely it will do any harm.

But what if we can stop loading it all together and only call it when needed “for user”, not for the machine-like PageSpeed Insight.

And here is where I will be implementing JavaScript delay.

JavaScript Delay

The delayer will make sure, that JavaScript will be loaded when is needed. It’s not only limited to Cookie Consent Banner and can be used with other scripts in a similar manner. Before you will implement it, make sure you test it well. It will not work on some JavaScript-based Lighthouse effect for images on the website but will work for Google Analytics.

JavaScript can be loaded with just <script> tag, with async parameter or defer. Each of them got pros and cons. The final decision is based on the importance of the script and its impact on the website.

Firstly, we will need to implement a delayer (ideally in the footer)

const autoLoadDuration = 5; //In Seconds
const eventList = ["keydown", "mousemove", "wheel", "touchmove", "touchstart", "touchend"];

const autoLoadTimeout = setTimeout(runScripts, autoLoadDuration * 1000);

eventList.forEach(function(event) {
    window.addEventListener(event, triggerScripts, { passive: true })

function triggerScripts() {
    eventList.forEach(function(event) {
         window.removeEventListener(event, triggerScripts, { passive: true });

function runScripts() {
    document.querySelectorAll("script[delay]").forEach(function(scriptTag) {
        scriptTag.setAttribute("src", scriptTag.getAttribute("delay"));

This short script will wait for user interaction by pressing the key, moving a mouse or touching the screen. This is the real interaction of the real user, not the machine that is “just” testing performance.

After that interaction, it will fire up all delayed scripts.

Now we need to decide which are they.

For this matter we changing the part responsible for loading our JavaScript as follow:

<script delay="cookie.js" defer></script>

You will see that src has been replaced with delay making <script> invalid for HTML validators. As in HTML there is no such parameter as delay, and src is required, the script will simply not load. And that is what we want.

Now our delayer implemented earlier will do its magic. When it will detect real user interaction, it will fire up the script by changing delay word (this can be any other word by the way) to scr making the <script> valid and working again.

And like that, after our page is loaded (when PageSpeed will output its result), our script will load.

There is also a fail-safe implemented there if there is no user interaction at all to load delayed script after 5 seconds either way after loading script (which was delayed using defer to load after the page is loaded). After 5 seconds properly designed website shall already be fully loaded.

In that scenario, our Cookie Banner will not be displayed at all, independently on status in browser localStorage. When you move the cursor, press a key or scroll the page, it will display for us and will hunt until we click the Ok button.

You can trace delayer behaviour by inspecting a website using the build-in Developer Tools in your browser (see tab Network).

If your website is using some heavy scripts (like heavy original Google Analytics script, if you not using Minimal Analytics, or even Ads pushed this way), by delaying them, you can significantly improve your website performance, at least in the eye of Google performance robots.

The above part related to JavaScript Delay, in short, is based on Delay Javascript to Boost Web Vitals Score by SpeedVitals. Have look at their post to get wider into that matter.