Push custom events to Google Tag Manager and Analytics

Continuing the discussion from Setup Google Tag Manager for Analytics:

:warning: This guide is a wiki. I am far from an expert in using Google Tag Manager or Google Analytics. If you are an expert at using GTM, feel free to edit the topic, or reply to the topic with suggestions for improvements. If there are particular events that you are wanting to track, let us know and we’ll see what can be done to push them onto the Data Layer.

To configure your Discourse site to push page view events to Google Tag Manager and Google Analytics, follow the steps outlined here: Setup Google Tag Manager for Analytics - admins - Discourse Meta. After completing those steps, you should see data about the title and URL of Discourse page views on your analytics page.

In this topic I will outline how to add custom events to the data that is sent to GTM and Analytics. Note that this topic assumes you have entered your site setting and updated your site’s content security policy script src setting to allow the Google scripts to run on the site.

Adding Discourse events to the GTM Data Layer

Note: when you enable GTM on Discourse by adding your tag manager ID to your Discourse gtm container id setting, a dataLayer object will be created. You can push new events onto the data_layer to make them available on GTM. If you are just testing the script below without having set your gtm container id , you’ll need to initialize the data_layer object in your script.

Discourse fires numerous events when users perform an action on the site. Some of these events will be useful for analytics. The code below handles the following events:

  • page:bookmark-post-toggled
  • post:created
  • topic:created
  • page:like-toggled
<script type="text/discourse-plugin" version="0.11.0">
// This should only be required if the gtm_container_id hasn't been set, but it is probably 
// best to include it to prevent errors if the gtm container id is ever removed from that setting.
window.dataLayer = window.dataLayer || [];

api.onAppEvent("page:bookmark-post-toggled", post => {
    if (post && post.bookmarked) {
        window.dataLayer.push({
            'event': 'postBookmarked',
            'postId': post.id
        });
    } 
});

api.onAppEvent("post:created", post => {
    if (post) {
        window.dataLayer.push({
        'event': 'postCreated',
        'postId': post.id
        });
    }
});

api.onAppEvent("topic:created", (post, composerModel) => {
    if (post) {
        window.dataLayer.push({
            'event': 'topicCreated',
            'topicCategory': composerModel.get("category.name")
        });
    }
});

api.onAppEvent("page:like-toggled", (post, likeAction) => { 
    let topic = post.topic;
    if (post && topic && likeAction && likeAction.acted) {
        window.dataLayer.push({
            'event': 'postLiked',
            'postId': post.id
        });
  }
});
</script>

In all events except the topicCreated event, the ID of the post that has been acted on is being added to the dataLayer object. The topicCreated event is an example of how other types of data can be added to the dataLayer . If you have followed all steps in Setup Google Tag Manager for Analytics - admins - Discourse Meta, you will also have page.title and page.url variables configure on GTM that can be used to add data about the page’s title or URL to the custom event on GTM.

Configure GTM

For the examples below, I’ll be configuring the postBookmarked event that’s been added to the dataLayer in the code example above.

Create a Trigger

Go to https://tagmanager.google.com/. Select Triggers from the side menu and click the New button to create a new trigger. Give your trigger a name, select Custom Event as the Trigger type and postBookmarked as the Event name . Set the trigger to fire on “All Custom Events”:

Create a Data Layer Variable

Select Variables from the GTM side menu. On the page’s User Defined Variables section, click the New button. Give the variable a recognizable name (you’ll be using it in the next step.) For Variable Type select “Data Layer Variable.” Set the Data Layer Variable Name to postId . This allows GTM to read the value of the postId that you have pushed onto the dataLayer . Make sure the Data Laver Version is set to “Version 2”:

Create a Universal Analytics Event Tag

Select Tags from the GTM side menu and click the button to create a new tag. Give your tag a name. For the Tag Type select Google Analytics: Universal Analytics . For the Track Type select Event . Give your tag a Category. That category will be displayed on your Analytics page when the event fires. The value of the Action field is what will get passed along with the event from GTM to Analytics. To send the postId that was configured in the previous step to Analytics, add that variable’s title, surrounded by {{}} to the form’s Action section. You’ll see that you can select this variable from the field’s drop down menu.

You can now either configure a settings variable and add it to the Google Analytics Settings field, or select “Enable overriding settings in this tag” and enter your Analytics tracking ID into the form:

Submit your changes

Before the data will be available on Analytics, you need to click the GTM Submit button to publish your changes. You should now be able to browse your site and see your actions in the Analytics Events section:

Todo

  • find the best way to track signup events
11 Likes

@simon Thanks for posting this. I appreciate the outline and would like some guidance on how to implement the javascript snippet you have shown in your post.

I’ve setup the default tracking mentioned through GTM for the virtualPageView event as well as the Page Title and Page URL values. I’ve attempted to add the script snippet provided, via GTM custom tag, and I’m not seeing the events outlined firing. :confused:

How should I be adding the javascript “plugin”?

Thanks for the assistance.

The best way to add it is to create a new theme component from your site’s Admin / Customize / Themes section:

Copy and paste the full code snippet from my post into that component’s /head section:

After saving your changes, add the theme component to all active themes on your site:

If you want to confirm that the code is being loaded, you could add a console.log statement to some of the events to make sure that they are firing. Another good tool for debugging this is the GTM preview mode:

When preview mode is enabled, you can see all tags that are pushed from Discourse to GTM.

Get in touch with the Discourse team through our private support system if you have trouble getting this to work.

3 Likes

Thank you - Works great. I converted it for use with Tealium TiQ instead of GTM.

You mentioned numerous events; is there a complete list of events I can reference beyond the examples shown here?

1 Like

There isn’t one yet, but I’ll put together a list tomorrow. In the meantime, are there specific events that you want to be able to track?

2 Likes
  • Registration
  • Login
  • Thread > Subscribe
  • Thread > Share
  • Post Reply > Share
  • Forum Search
2 Likes

Here is the list of events I’ve been able to find that can be targeted with the onAppEvent method:

  • page:changed
  • page:bookmark-post-toggled
  • post:created
  • topic:created
  • page:like-toggled
  • topic:flag-created
  • post:flag-created
  • page:topic-loaded
  • page:compose-reply

I am not finding a way to use this method to track the signup or registration events. I’ve asked about this before on Meta, so I am fairly sure there isn’t a user:created, or user:logged-on event that can be hooked into. It looks like the best approach for tracking signups may be to track visits to the /users/account-created page with something like this:

api.onPageChange((url, title) => {
    if (url === '/u/account-created') {
        window.dataLayer.push({
            'event': 'newUserCreated'
        });
    }
});

Unfortunately, that approach will only work for users that are created by submitting the Discourse login form. It also doesn’t give you any details about the user that was created, or tell you if the user activated their account by responding to the Discourse account activation email.

It is also possible to track Discourse click events to push information onto the data layer. This approach could be used with either the Sign Up or Log In buttons. The downside of this is that all it is telling you is that the buttons were clicked. It doesn’t tell you if the user went on to activate their account, or login to the site.

The click tracking approach can also be used to get details about shared topics and posts.

<script type="text/discourse-plugin" version="0.11.0">
window.dataLayer = window.dataLayer || [];

let main = document.getElementById("main");
if (!main) {
    return;
}
let currentUrl; // Setting this in the call to onPageChange, then using it in the clickHandler method.
    
api.onPageChange((url, title) => {
    currentUrl = url
    if (currentUrl === '/u/account-created') {
        window.dataLayer.push({
            'event': 'newUserCreated'
        });
    }
});

let clickHandler = function(e) {
let target = e.target;
if (target) {
    if (target.closest('.login-button')) {
        window.dataLayer.push({
            'event': 'userLoggedIn'
        });

    } else if (target.closest('.sign-up-button')) {
        window.dataLayer.push({
            'event': 'newUserCreated'
        });

    } else if (target.closest('#topic-footer-button-share-and-invite')) {
        window.dataLayer.push({
            'event': 'topicShared',
            'url': currentUrl
        });

    } else if (target.closest('.post-controls .share')) {
        let shareUrl = $(target).parent().data('share-url')
        window.dataLayer.push({
            'event': 'postShared',
            'post': shareUrl
        });
    }
}

return false;
};

main.addEventListener("click", clickHandler);
main.addEventListener("touchstart", clickHandler);
</script>

I’d still like to find a better way of passing the login and signup events to analytics. I’ll look into finding a way of tracking search events as well.

5 Likes

@simon Did you figure out how to fire events on signup / registration? Curious about this as well.

2 Likes

In case others find this useful, here are a few more events that may be targeted with the onAppEvent method:

  • poll:voted
  • group:join
  • group:leave
  • post-stream:posted
  • post-stream:refresh
  • bookmarks:changed
  • topic:bookmark-toggled
  • topic-entrance:show
  • topic:scrolled
  • topic:current-post-changed
  • topic:current-post-scrolled
  • cta:shown
  • card:open
  • card:close
  • user-card:show
  • page:like-toggled
  • url:refresh
4 Likes

I’ve installed the javascript plugin code as a component that has the script in the Head section of the Customize > Themes. I’ve added the component to the active theme. However, I don’t see any indication that it is creating any events in the dataLayer when I am using GTM preview mode. I’ve tried to bookmark, like, create posts, etc., but nothing is being pushed to the dataLayer.

How can I confirm that the component script is live on the site? When I view source, I see the name appear below the , but I don’t see any other details of the script contents.

In case you haven’t already done it, make sure to add your Google Tag Manager container id to your Discourse site’s gtm container id setting:

With that setting in place, Discourse will automatically load the required GTM script. This topic’s OP should be updated to include that detail. I’ll update it later this week if no one else gets to that before me.

First, make sure that the theme component you created is included on all themes that users can select on your site. If it’s working correctly, you should see results in GTM preview mode. If you’re still not sure it’s working, try adding some console.log statements to the script. For example:

api.onAppEvent("post:created", post => {
    console.log("the post_created event has been triggered");
    if (post) {
        window.dataLayer.push({
        'event': 'postCreated',
        'postId': post.id
        });
    }
});

Let us know if you are still not getting any results or if any of the custom events are failing to be triggered as expected.

1 Like

Here’s a screencast of our setup. It appears I have everything in place, but nothing is showing up in the console log when I like or bookmark posts.

https://watch.screencastify.com/v/O0YegRkJGCXjNJXe6KwW

page:bookmark-post-toggled has been replaced with bookmarks:changed.

You might want something like:

api.onAppEvent("bookmarks:changed", (data, {target, targetId}) => {
    if (!data) {
        // bookmark deleted
        return;
    }
    
    // "target"   contains either "topic" or "post" identifier.
    // "targetId" contains either topic ID or post ID.
    
    if (target === "post") {  
        window.dataLayer.push({
            'event': 'postBookmarked',
            'postId': targetId
        });
    }
});

I’m not familiar with custom events, but for the topic, you could do the following:

if (target === "topic") {  
    window.dataLayer.push({
        'event': 'topicBookmarked',
        'topictId': targetId
    });
}
1 Like

A couple of configuration thoughts:

It’s possible that you can resolve some of your CSP errors if you Use nonces in Google Tag Manager scripts.

Since you’re using GTM, you should also be sure that the ga universal tracking code site setting is empty. GTM will load your tracking script so this is unnecessary.

I use a Chrome add-on called Simple Data Layer Viewer, which shows you the dataLayer variable info. For your landing page, I’m not seeing any:

[
    {},
   ▲ {
        "gtm.start": 1682113931034,
        "event": "gtm.js",
        "gtm.uniqueEventId": 1
    },
   ▲ {
        "event": "gtm.dom",
        "gtm.uniqueEventId": 3
    },
   ▲ {
        "event": "gtm.scrollDepth",
        "gtm.scrollThreshold": 90,
        "gtm.scrollUnits": "percent",
        "gtm.scrollDirection": "vertical",
        "gtm.triggers": "7",
        "gtm.uniqueEventId": 9
    },
   ▲ {
        "event": "gtm.load",
        "gtm.uniqueEventId": 10
    }
]

I would revisit the Variables section of Set up Google Tag Manager for Analytics to be sure you have it all configured correctly.

UPDATE:

I’m going to leave this post for reference, but I do see some events now so perhaps there was just a glitch initially:

[
    {},
   ▲ {
        "gtm.start": 1682115996464,
        "event": "gtm.js",
        "gtm.uniqueEventId": 1
    },
   ▲ {
        "event": "virtualPageView",
        "page": ▲ {
            "title": "RMW Commerce Community - Your Trusted eCommerce Industry Community",
            "url": "/"
        },
        "gtm.uniqueEventId": 3
    },
   ▲ {
        "event": "gtm.dom",
        "gtm.uniqueEventId": 5
    },
   ▲ {
        "event": "gtm.load",
        "gtm.uniqueEventId": 11
    },
   ▲ {
        "event": "gtm.click",
        "gtm.element": "<a href=\"/t/scotch-soda-acquired-by-bluestar-alliance/966\" role=\"heading\" aria-level=\"2\" class=\"title raw-link raw-topic-link\" data-topic-id=\"966\">Scotch &amp; Soda Acquired by Bluestar Alliance</a>",
        "gtm.elementClasses": "title raw-link raw-topic-link",
        "gtm.elementId": "",
        "gtm.elementTarget": "",
        "gtm.elementUrl": "https://community.rmwcommerce.com/t/scotch-soda-acquired-by-bluestar-alliance/966",
        "gtm.uniqueEventId": 12
    },
   ▲ {
        "event": "virtualPageView",
        "page": ▲ {
            "title": "Scotch & Soda Acquired by Bluestar Alliance - Fundraising and Acquisitions - RMW Commerce Community",
            "url": "/t/scotch-soda-acquired-by-bluestar-alliance/966"
        },
        "gtm.uniqueEventId": 13
    },
   ▲ {
        "event": "gtm.historyChange-v2",
        "gtm.historyChangeSource": "pushState",
        "gtm.oldUrlFragment": "",
        "gtm.newUrlFragment": "",
        "gtm.oldHistoryState": ▲ {
            "path": "/"
        },
        "gtm.newHistoryState": ▲ {
            "path": "/t/scotch-soda-acquired-by-bluestar-alliance/966"
        },
        "gtm.oldUrl": "https://community.rmwcommerce.com/",
        "gtm.newUrl": "https://community.rmwcommerce.com/t/scotch-soda-acquired-by-bluestar-alliance/966",
        "gtm.triggers": "8",
        "gtm.uniqueEventId": 15
    }
]

Thanks, Mark! The nonce addition to all custom HTML tags does appear to have resolved all of JS errors in the console.

With respect to the onAppEvent triggers, I do see GA4 events being fired for the post:created and topic:created triggers, but I can’t seem to get post bookmark and like to work.