Docs menu

DocsFor developers

Banner JavaScript and Google Consent Mode

The banner script holds back tracking until a visitor agrees, tells your own code what they chose, and sets Google Consent Mode for your Google tags. This page is for whoever looks after your website's code.

Load the script first

Paste the banner code as the first item inside <head> on every page, without async or defer. It must run before any tag it controls, so nothing loads early.

<script src="https://cdn.complyzero.com/widget.js" data-id="cz_widget_your_banner_id"></script>
  • data-id is your banner's public ID. Copy the whole line from the Embed tab of Consent Banner.
  • data-lang is optional and picks the language the banner opens in, such as data-lang="hi" for Hindi.
  • If your site uses a Content Security Policy with nonces, give the tag your nonce. The banner uses it for every script it releases.

Check installation on the Embed tab warns you if the tag loads late, or if a tool on the page can never run because your banner has no purpose for its category.

What the banner holds by itself

The banner recognises about 50 well-known tools, such as Google Analytics, Microsoft Clarity, Hotjar, Meta, LinkedIn, Reddit, YouTube, Vimeo, Google Maps and the common chat tools. Each one waits until the visitor allows it, then runs without a reload. Your Cookies page decides how:

  • A rule you set for the tool's address decides first.
  • Then how you classified the tool's cookies. If they're all Necessary, it always runs. If they're mapped to a purpose, it waits for that purpose.
  • A tool you haven't classified waits for every purpose of its usual category.

Some tools are handled in their own way:

  • Google: the Tag Manager container always loads, and what it adds to the page is held like anything else. gtag.js follows the product its ID names: G- waits for Analytics, AW- and DC- for Marketing.
  • Chats: a chat waits like any other tool, and its own chat button appears once the visitor allows it. If your chat must be there for every visitor, classify its cookies as Necessary.
  • Videos and maps: a held frame the visitor can see gets a box in its place, naming where it comes from, with a button that opens their privacy choices and a link to open it in a new tab.
  • Requests: a held request fails the way an unreachable server does, so your page's own error handling runs.

Tools the banner doesn't recognise run as usual, until you classify them on the Cookies page or tag them as below.

Hold a script or frame yourself

A script written into your page's HTML starts loading before any script can stop it. For one that must not run before consent, change its type to text/plain and say which category it belongs to:

<script type="text/plain" data-czcat="analytics"
        src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>

For a frame or image written into your HTML, put its address in data-cz-src instead of src:

<iframe data-cz-src="https://www.youtube-nocookie.com/embed/VIDEO_ID" data-czcat="marketing"
        width="560" height="315" title="Our product tour"></iframe>
  • The categories are functional, analytics and marketing. The script or frame runs once every purpose in that category is on.
  • data-czcat="essential" marks something your site needs to work, such as a sign-in or payment script. It always runs.
  • To tie it to one purpose instead, use data-cz-choice with the purpose's ID, which is in the address bar when the purpose is open in Purposes.

Read the visitor's choice

The script adds window.ComplyZero:

ComplyZero.getConsent();            // { state, purposes, receiptReference }
ComplyZero.hasConsent("analytics"); // a category, or a purpose's ID
ComplyZero.openPreferences();       // opens the banner's settings, for a "Cookie settings" link
ComplyZero.status();                // in the browser console: what's held on this page, and why

hasConsent is true when the visitor's latest choice says yes: to that purpose, or to every purpose of that category. A choice applies the moment the visitor makes it.

purposes maps each purpose's ID to whether it's on right now. state is one of:

  • accepted: the choice is recorded with us, and receiptReference names its record.
  • pending: the choice already applies, and its receipt is on its way to us. If the connection drops, it's sent again on the next page.
  • not_accepted: we couldn't record the latest choice, so the visitor is asked again and the safe defaults stay in place.
  • local_restriction: there's no choice yet, or the browser blocks storage, so only purposes that need no consent run.

React when the choice changes

Each time the choice changes, the script fires a complyzero:consent event on window, with the same object as getConsent():

window.addEventListener("complyzero:consent", (event) => {
  if (ComplyZero.hasConsent("analytics")) startAnalytics();
});

Google Tag Manager

The script also pushes the choice to the data layer as the event complyzero_consent_update, with the same object under complyzero. In Tag Manager, use a Custom Event trigger with that name, and read values with a Data Layer Variable such as complyzero.state.

Before any Google tag runs, the script sets Consent Mode defaults: ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage and personalization_storage are denied, and security_storage is granted. Google tags wait up to half a second for the visitor's stored choice, so a returning visitor's first page is measured correctly. It works with gtag and with the data layer queue.

When the visitor chooses, the script sends an update. Each purpose says which Google types it covers, under Google Consent Mode types in Purposes. A type is granted only when every purpose that covers it is on, and a type no purpose covers stays denied.