Skip to content

Custom Event labeling

Vitor Gonçalves edited this page Apr 15, 2026 · 13 revisions
  • There is a large JavaScript that handles all this work for our main websites.
  • By principle, the DOM works as source of truth
  • It relies on custom definitions to be attributed via templates as HTML properties.
    • data-ga-action (defined at the link <a>)
    • data-ga-category (can be defined on a parent for all internal links)
    • data-ga-subcategory (can be defined on a parent for all internal links)
    • data-ga-label (defined at the link <a>)
    • data-ga-position (defined at the link <a>)
  • In absence, it will deduce from the HTML context.
  • There is also a function that pre-populates the HTML with a set of properties when it is too difficult to work on the templates or to deduce from context.
  • Visual documentation on how some main screens are tracked can be seen in this Figma file.

To-do

  • At the moment we cannot track sessions between different sub-domains. So if someone started a session on guides, and ended in the catalog, we won't know. How many people come to the catalog from google vs guides? We don't know. There is a GA4 solution which can be automatically or 'manually' with our own code. We tried automatically but it breaks some links and tampers with persistent identifiers because it adds a parameter to each link on a page from any sub-domain that leads to another, which also affects links that eventually lead to a redirect. The best solution would be to implement a small script that fetches the session and user id from GA4, selectively injects it into the URL (or via another method like localstorage) and loads it back to GA4 in the next page. So it could be done in the same script.

Websites that call the script

Making changes

Making changes to the script can be tricky. Particularly VuFind labeling has a delicate series of conditions that depends on context, mostly CSS classes.

Making changes to HTML templates is trivial. Just make sure to follow the Schema below. Values for category, subcategory, and label can defined at an item level or at a parent level.

Some example of rules for populating HTML dynamically

// Example rules for applyHtmlProperties
var ExampleHtmlPropertyRules = [
    // 1) Set a data attribute on the first link inside .record div 
    // using the text of the first heading in the same div as value
    // only when on guides.lib.uchicago.edu
    {
        location: 'guides.lib.uchicago.edu',
        selector: '.record',
        childSelector: 'a',
        attribute: 'data-ga-subcatery',
        useFirstHeading: true,
        apply: 'first'
    },
    // 2) Set the longest class name from the library widget on the home page
    // as a data attribute to each link inside the widget
    {
        location: LOCATIONS.LIB,
        selector: '#widget-featured-library-expert',
        childSelector: 'a',
        attribute: 'data-test-label',
        valueFn: function ($items, $target) {
            var mainClassAttr = $items.attr('class') || '';
            var mainClass = '';
            if (mainClassAttr) {
                var classes = mainClassAttr.split(/\s+/).filter(function (c) { return c; });
                if (classes.length) {
                    mainClass = classes.reduce(function (a, b) { return a.length >= b.length ? a : b; }, '');
                }
            }
            if (!mainClass) {
                mainClass = $target.text().trim();
            }
            return mainClass;
        },
        apply: 'each'
    },
    // 3) Example: derive a role value from a class on the target element (assumes class like 'role-admin' => 'admin')
    {
        selector: '.some-role-element',
        attribute: 'role',
        value: 'custom-value',
        overrideIfExists: true,
        apply: 'each'
    }
];

New Schema (September 25, 2025)

  • name (action)
    • click (most things)
    • tab (for role=”tab”; and for the navbar dropdowns)
    • dropdown (data-toggle="dropdown”)
  • event_category
    • Navigation
    • Main
    • Sidebar
    • Footer
    • Floating (Alert banners, feedback button)
    • Main Search Widget
    • Global Navbar
  • event_subcategory
    • Recognizable content, function blocks, components.
    • Distinguishes links in the same page with the same inner text or same data-ga-label.
    • Typically has a conceptual term like: List, Widget, Table, Form, Toolbar, Pagination
    • ex: Global Navbar, Footer, Left Sidebar, Right Sidebar, Search Widget, *** Widget, Recent News, News List, Search Results List, Your Account Menu,
    • defaults to: .getAttribute('aria-labelledby'), .closest('[id]').getAttribute('id')
  • event_label
    • The actual link or button text, image alt text, or aria-label, role of the link.
    • Where links are typically highly dynamic, the role of the link will be preferred.
    • Generic: .getAttribute('aria-label'), .textContent, .getAttribute('title'), .getAttribute('alt'), 'Unknown'
    • VuFind: 'Title', 'Author', 'Holding', 'Save Record', 'Unknown'
    • Guides: 'Guide Name', 'Guide Author', 'Guide Subject', 'Guide Link', 'Guide Page Title', 'More Button', 'Unknown'
  • click_position
    • Position of the click on a list. Typical for search results, recent news, exhibiti index, etc.
    • can be based on the index of a <li> item, a <div> as a ('.newsblock, article'), a row on a table
  • event_indecision_count
    • Counts how many times a user has clicked on tabs or dropdowns before making a final selection.
  • event_option
    • Adds any checkbox or radio button selected to the main link, like search options in the home page search widget.
    • Only applied to the main search widget on the homepage.

Special Cases

  • Main Search Widget, has it’s own category
  • Global Navbar, has it’s own category
  • Left Sidebar (typically navigation)
  • Guides has a left sidebar where one part is navigation, and below is “secondary content” so Sidebar.
  • Pagination? cat: Main, subcat: Pagination

Clone this wiki locally