Google Analytics 4 Custom Events and Parameters with gtag.js

Here's another PSA for anyone getting started with, or migrating to Google Analytics 4, with custom events and parameter collection via gtag.js.

For starters, below are the key resource links - which seem to be pretty much all over the place at Google.

Special note that if you want your custom parameters to be available as custom dimensions in Analysis Hub - you DO NOT need to register the parameters in your gtag.js config using a custom map as you did previously with Universal Analytics.  You DO however, still need to register the parameters as custom dimensions. Note that while you will you see your custom event and parameters in the debug and real-time streams - it can take a day or longer for the custom parameters to be available to the Analysis Hub and custom reports. Also be sure to read the help text when creating custom dimensions. In particular the fact that you can type in the name of your custom parameter even if it's not on the list of available parameters yet - e.g., "Choose a parameter or property from the list or enter the name of a parameter or property you'll collect in the future."

Here are the key resources - which I found in various places, followed by a helper script I use to collect custom event data.

  1. General implementation guide for gtag.js (note the section on custom dimensions and metrics). https://support.google.com/analytics/answer/9310895?hl=en
  2. gtag.js API reference - https://developers.google.com/gtagjs/reference/api
  3. gtag.js Parameter reference - which shows that event_callback and event_timeout work as before (for example if you were previously using dataLayer.push) - https://developers.google.com/gtagjs/reference/parameter

Here's my basic gtag.js configuration...

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-YOURPROPERTYID"></script>
<script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('set', {'cookie_flags': 'SameSite=None;Secure'});
      gtag('config', 'G-YOURPROPERTYID');
</script>

And here's a general purpose event and parameter collection script, with both callback and timeout settings if required. If the click occurs on an anchor element that has target="_blank" there's no need to preventDefault or use the callback since the script and event parameter collection will have time to complete on the current page.

(function () {
  'use strict';

  const handleGAClickEvent = function (event) {
      if (typeof gtag !== 'undefined') {

      // Get the element the event listner is bound to
      const element = event.currentTarget;

      // Check if the target is an anchor element, and if so prepare
      // extra variables
      let callback;
      if (element instanceof HTMLAnchorElement) {
        const target = element.getAttribute('target');
        // Get the href - checking for possible SVG/XML links via xlink:href
        const href = element.getAttribute('href') || element.getAttribute('xlink:href');
        // Only define a callback and preventDefault if the element is an
        // anchor element and target is NOT _blank.
        if (!target || target !== '_blank') {
          // preventDefault and the callback will allow event data
          // collection to complete before the window location is set
          // (i.e. before navigation away from the current page)
          event.preventDefault();
          callback = () => window.location.href = href;
        }
      }

      // Get the event name
      let gaEventName = null;
      for(const data in element.dataset) {
        if(data === 'gaEvent') {
          gaEventName = element.dataset[data];
        }
      }

      // Get the event parameters and record the event
      if(gaEventName) {
        let gaParameters = {};
        for(const data in element.dataset) {
          if(data.startsWith('ga') && data !== 'gaEvent') {
              let parameterName = `${gaEventName}_${data.substring(2).toLowerCase()}`;
              gaParameters[parameterName] = element.dataset[data];
          }
        }

        try {
          // https://developers.google.com/gtagjs/reference/api#event
          // https://developers.google.com/gtagjs/reference/parameter
          gtag('event', gaEventName, {
            ...gaParameters,
            'event_callback': callback,
            'event_timeout': 1500,
            'debug_mode': true
          });
        } catch (exception) {
          console.error('Error in ga4-analytics.js:');
          console.error(exception);
        }
      }
    } else {
      console.error('Error in ga4-analytics.js: gtag not found.')
    }
  }

  // Get all elements that have information for a Google Analytics event 
  // in data-ga atttibutes
  const elements = document.querySelectorAll('[data-ga-event]');
  for(const element of elements) {
    element.addEventListener("click", handleGAClickEvent, false);
  };
})();

Here's an example link decorated with the required attributes for a custom 'download' event, with category, label, and url as parameters.

<a data-ga-event="download" data-ga-category="publication downloads" data-ga-label="Project Report 2021" data-ga-url="/resources/0000098-0001-en.pdf" href="/resources/0000098-0001-en.pdf" title="0000098-0001-en.pdf" type="application/pdf; length=560790" target="_blank" rel="noopener">
...
</a>

Hope this helps!

Add new comment

The content of this field is kept private and will not be shown publicly.

Filtered HTML

  • Web page addresses and email addresses turn into links automatically.
  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.