Aller au contenu

Référence du tracker t.js

Le tracker est servi par le Worker de collecte sur GET /js/t.js (Cache-Control: public, max-age=86400, stale-while-revalidate=604800 + ETag). ~1,5 Ko min+gzip (budget CI : 3 Ko). Zéro dépendance, IIFE, tout le code sous try/catch silencieux : il ne lève jamais d’erreur dans la page hôte.

<script defer src="https://elvn.live/js/t.js" data-site="SITE_PUBLIC_ID"></script>
Attribut Requis Effet
data-site oui Identifiant public du site. Sans lui, le tracker ne fait rien.
data-api non Endpoint de collecte custom (ex. /stats/e en proxy first-party). Défaut : <origine du script>/api/e, ou /api/e si l’origine est introuvable.
data-spa="off" non Désactive le suivi des navigations SPA.
data-outbound="off" non Désactive les événements outbound / download.
data-engage="off" non Désactive l’événement engage (temps actif + scroll).
data-dev non Autorise le tracking sur localhost / 127.x / [::1] (sinon le tracker reste muet en local). Attribut de présence, sans valeur.

Les trois attributs spa / outbound / engage ne se désactivent qu’avec la valeur exacte "off".

  • Pageview au chargement, puis à chaque navigation SPA : history.pushState / history.replaceState patchés + popstate, dédoublonnés si le pathname ne change pas.
  • Liens sortants : clic sur un <a href> dont le hostname diffère de la page → événement outbound avec {url} en props.
  • Téléchargements : clic sur un lien interne dont le path se termine par une extension de fichier (pdf, zip, dmg, exe, pkg, msi, deb, rpm, apk, iso, rar, 7z, gz, tgz, tar, csv, doc(x), xls(x), ppt(x), txt, mp3/mp4, mov, avi, wmv, wav) → événement download avec {url}.
  • Engagement : au premier passage de l’onglet en arrière-plan (visibilitychange → hidden) de chaque pageview, un événement engage avec {t: temps actif cumulé en ms, d: profondeur de scroll max en %}. Le temps actif compte au plus 30 s après la dernière interaction (clic, touche, scroll). Réarmé à chaque navigation SPA. Sert au calcul réel du bounce et de la durée de visite.

JSON ≤ 4 096 octets sur POST /api/e :

{ "s": "SITE_PUBLIC_ID", "u": "/chemin?utm_source=…", "r": "https://referrer…", "w": 1440, "l": "fr-FR", "e": "nom-evenement", "p": { "plan": "pro" } }
Champ Contenu
s Identifiant public du site (data-site).
u Path + query filtrée côté client : seuls les paramètres utm_*, ref et source sont conservés.
r document.referrer.
w screen.width.
l navigator.language.
e Nom d’événement (absent pour un pageview).
p Props de l’événement (absent si aucune).

Transport : navigator.sendBeacon (corps texte → pas de préflight CORS), fallback fetch(…, { keepalive: true }). Aucun retry client : un événement perdu est perdu. Les envois déclenchés avant la fin du parsing du document sont mis en file et rejoués à DOMContentLoaded.

Le Worker re-valide tout côté serveur et répond toujours 202, corps vide (événement accepté ou rejeté — aucun oracle).

Pour appeler window.elvn() avant que t.js soit chargé, ajoutez ce stub avant le snippet — les appels sont rejoués à l’initialisation :

<script>
window.elvn =
window.elvn ||
function () {
(window.elvn.q = window.elvn.q || []).push(arguments);
};
</script>
window.elvn("signup", { plan: "pro" });
window.elvn("newsletter-open");

Contraintes (validées côté client ET re-validées par le Worker)

Section intitulée « Contraintes (validées côté client ET re-validées par le Worker) »
  • Nom : minuscules a-z, chiffres, _, -, 1 à 64 caractères (/^[a-z0-9_-]{1,64}$/). Le tracker met en minuscules automatiquement ; un nom invalide est abandonné en silence.
  • Noms réservés — ne pas les utiliser pour des événements custom : pageview, engage, outbound, download.
  • Props : objet plat uniquement. Valeurs scalaires : chaînes (tronquées à 500 caractères), nombres finis, booléens. Objets imbriqués, tableaux, null, NaN/Infinity → ignorés silencieusement. Clés de 1 à 64 caractères. Le JSON des props doit tenir en 2 048 octets (payload total ≤ 4 096 octets), sinon le Worker rejette l’événement.
  • Jamais de données personnelles dans les props (email, nom, identifiant…) — c’est la responsabilité de l’intégrateur et une condition de l’exemption CNIL (voir Vie privée).

Délégation de clic — l’ancêtre le plus proche portant l’attribut gagne, y compris quand le clic touche un élément imbriqué :

<button data-elvn-event="cta-hero">Essayer</button>

Les goals de type « event » du dashboard matchent ces noms d’événements.

Rien à configurer : pushState/replaceState sont patchés et popstate écouté. Un pageview est envoyé à chaque changement de pathname (même path → dédoublonné), et l’événement engage est réarmé. Pour un routeur exotique qui ne passe pas par l’History API : il n’existe pas d’API de pageview manuelle — gardez l’History API (ne mettez data-spa="off" que si vous assumez de perdre les navigations internes).

Le tracker s’arrête de lui-même quand :

  • navigator.webdriver === true (automatisation) ;
  • la page est dans une iframe (window.top !== window.self) ;
  • la page tourne sur localhost (localhost, 127.x.x.x, [::1]) sans data-dev ;
  • opt-out visiteur : localStorage.elvn_ignore === "true".

Côté serveur, le Worker rejette en silence (toujours 202) : sites inconnus/inactifs, origine non conforme au domaine déclaré (sous-domaines acceptés), bots (User-Agent + ASN datacenter), dépassement du rate limit (100 evt / 10 s par site + IP tronquée), et les requêtes portant Sec-GPC: 1 si le site respecte Global Privacy Control (option par site, activée par défaut).

Snippet opt-out à proposer aux visiteurs (voir la page vie privée pour la version complète) :

<button onclick="localStorage.elvn_ignore = 'true'; this.textContent = 'Vous n’êtes plus compté.'">
Ne plus me compter dans les statistiques
</button>

Ré-inclusion : localStorage.elvn_ignore = 'false' (ou suppression de la clé), puis rechargement.

Si le site applique une Content Security Policy, autorisez uniquement :

  • script-src : https://elvn.live (ou le path du proxy first-party) ;
  • connect-src : https://elvn.live (ou l’endpoint data-api du proxy).