Skip to content

Instantly share code, notes, and snippets.

@mindplay-dk
Created November 21, 2024 12:45
Show Gist options
  • Select an option

  • Save mindplay-dk/8154b3d6583eec36bd82e11c741ca585 to your computer and use it in GitHub Desktop.

Select an option

Save mindplay-dk/8154b3d6583eec36bd82e11c741ca585 to your computer and use it in GitHub Desktop.
Simplified GA4 tracking script
// Simplified GA4 tracking script
// based on https://gist.github.com/idarek/9ade69ac2a2ef00d98ab950426af5791
(() => {
const tid = "G-XXXXXXXXXX"; // set your GA4 Measurement ID here
function serialize(input) {
const output = {};
for (const key in input) {
if (input[key] != null) {
output[key] = input[key];
}
}
return (new URLSearchParams(output)).toString();
}
// TODO add timestamp to storage state and clear the session ID (sid)
// if the last track() call was more than 30 minutes ago
// (test this with an initial 30 second timeout)
// add expiration timeout constants for both client and session IDs.
// TODO use an object to store the values instead of localStorage,
// write the whole object to localStorage at the end of track()
// and store the whole state in a single JSON storage item.
const get = (name, init) => localStorage[name] || (init && set(name, init()));
const set = (name, value) => localStorage[name] = value;
const createID = () => Math.floor(Math.random() * 2147483647) + 1;
const getTime = () => Math.floor(Date.now() / 1000);
const _p = createID(); // Page ID
let _s = 1; // Sequence number
const CLIENT_ID = "ga_cid";
const SESSION_ID = "ga_sid";
const SESSION_COUNT = "ga_sct";
const seg = get(SESSION_ID) ? 1 : 0; // Session engaged
function track(en, params = {}) {
// First Visit
const _fv = get(CLIENT_ID) ? null : 1;
// Generate or retrieve client ID
const cid = get(CLIENT_ID, () => createID() + "." + getTime());
// Session start
const _ss = get(SESSION_ID) ? null : 1;
// Session ID
const sid = get(SESSION_ID, getTime);
// Session count
const sct = _ss // session started?
? set(SESSION_COUNT, parseInt(get(SESSION_COUNT, () => "0")) + 1) // increment session count
: get(SESSION_COUNT); // report current session count
const data = serialize({
v: 2, // Protocol Version
tid, // Measurement ID
_p, // Page ID
cid, // Client ID
ul: (navigator.language || "").toLowerCase(), // User Language
sr: (screen.width + "x" + screen.height), // Screen Resolution
_s: _s++, // Sequence number
sid, // Session ID
sct, // Session count
seg, // Session engaged
dl: document.location.origin + document.location.pathname + document.location.search, // Document Location (without fragment)
dr: document.referrer,
dt: document.title,
en, // Event Name
_fv, // First Visit
_ss, // Session Start
_dbg: 1, // Debug Mode
// custom event params:
...Object.fromEntries(
Object.entries(params).map(([key, value]) => [`ep.${key}`, value])
)
});
const url = "https://www.google-analytics.com/g/collect";
// Send the hit using the most appropriate available method
if (navigator.sendBeacon) {
navigator.sendBeacon(url + "?" + data);
} else {
fetch(url + "?" + data, {
method: "POST",
keepalive: true
});
}
}
// Send the page view
track("page_view");
window.track = track;
})();
@mindplay-dk
Copy link
Author

I was wondering if we could also make the script GDPR compliant.

I don't want to track users, or pester them for permissions - I just want to use GA to count page views, etc.

  • Instead of using a cookie with a 2-year life span, it uses sessionStorage, so that we're no longer tracking users, and instead only tracking the visit.
  • If the user keeps a tab open for more than 12 hours, we also cycle the client ID, ensuring we count two days of activity as two visits.
((document, location, navigator) => {
  // Generate a new client ID
  const generateCid = () => ~~(2147483648 * Math.random()) + "." + ~~(Date.now() / 1000);

  // Get or generate a new client ID, rotating it every 24 hours
  const getSessionCid = () => {
    let cid = sessionStorage.getItem('_ga_session_id');
    const cidTimestamp = parseInt(sessionStorage.getItem('_ga_session_id_timestamp') || '0');

    // Rotate the client ID every 24 hours
    if (!cid || (Date.now() - cidTimestamp) > 12*60*60*1000) {
      cid = generateCid();
      sessionStorage.setItem('_ga_session_id', cid);
      sessionStorage.setItem('_ga_session_id_timestamp', Date.now().toString());
    }

    return cid;
  };

  window.track = (type, ec, ea, el, ev) => {
    const cid = getSessionCid();
    const data = {
      v: 1,
      tid: "UA-XXXX-Y", // replace with your tracking id
      aip: 1,
      cid,
      t: type,
      dr: document.referrer,
      dt: document.title,
      dl: location.href,
      ul: navigator.language.toLowerCase(),
      sr: `${screen.width}x${screen.height}`,
      vp: `${innerWidth}x${innerHeight}`
    };

    if (ec) data.ec = ec;
    if (ea) data.ea = ea;
    if (el) data.el = el;
    if (ev) data.ev = ev;

    navigator.sendBeacon("https://google-analytics.com/collect", new URLSearchParams(data));
  };

  track("pageview");
})(document, location, navigator);

This will produce different (higher) unique visitor numbers, but I think that's probably okay, as long as we understand that what we mean by unique visitors is now slightly different from the usual Google Analytics definition.

Might want to integrate this idea into the first script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment