Dincolo de pageview: urmărește ce fac chiar cititorii.
window.__afm.track(name, props?) înregistrează un eveniment ales de tine — o redare video, o înscriere la newsletter, un vot la sondaj — prin același tracker de ~2,5KB deja instalat. Fără script suplimentar, fără apel de inițializare, fără comutator în dashboard. Pagina aceasta e referința completă: API-ul, șase rețete gata de folosit, cum să denumești evenimentele corect și exact unde apar cifrele.
Nimic de configurat
Dacă scriptul trackerului din ghidul principal de instalare e deja pe pagină, evenimentele personalizate nu mai au nevoie de nimic altceva — fără comutator per site, fără script separat, fără pas de build.
<script defer src="https://t.pageview.ro/tracker.v1.js"
data-site="pk_your_public_key"></script>
<script>
window.__afm.track("newsletter-signup", { placement: "footer" });
</script>Fără configurare
track() face parte din același tracker deja instalat. Nu există niciun flag de activat și nimic de pornit per site.
Apare în câteva secunde
Evenimentul trimite un beacon imediat. Apare pe cardul Events al tabloului Realtime în câteva secunde, iar în raportul Events la scurt timp după.
Sigur de apelat devreme sau des
track() nu aruncă niciodată eroare în pagina ta. Apelurile făcute înainte ca trackerul să termine încărcarea sunt puse în coadă și livrate automat, iar un buget per vizualizare de pagină limitează apelurile scăpate de sub control, deci o eroare din codul tău nu poate inunda propriul tău raport.
Funcționează de oriunde
Un handler de click, evenimentele proprii ale unui player video, un efect React, un script inline dintr-o temă WordPress — track() e o funcție globală simplă, fără legătură cu vreun framework anume.
API-ul track()
O singură funcție, două argumente. Tot restul e validare la care nu trebuie să te gândești: intrarea invalidă este ignorată silențios, niciodată trimisă pe jumătate.
window.__afm.track(
name: string,
props?: Record<string, string | number | boolean>
): void| Regulă | Ce înseamnă |
|---|---|
| nameobligatoriu | Minuscul, respectând ^[a-z0-9][a-z0-9_.:-]{0,63}$ — litere, cifre, cratime, underscore, puncte și două puncte, începând cu o literă sau o cifră, cel mult 64 de octeți. Orice altceva înseamnă că tot evenimentul e ignorat, nu trimis parțial. |
| props | Perechi cheie → valoare, opționale. Șirurile, numerele și boolean-ii sunt transformate automat în text — nu apelezi tu String(). Maximum 10 intrări; ce depășește a 10-a intrare este ignorat, nu combinat. |
| chei prop | Același set de caractere ca numele evenimentului, cel mult 32 de octeți. O cheie invalidă elimină doar acea intrare — restul evenimentului tot se trimite. |
| valori prop | Cel mult 200 de octeți de text UTF-8, odată transformate în text. O valoare prea mare este ignorată complet — niciodată trunchiată silențios — deci trunchiază tu textul liber lung (ca un termen de căutare) înainte de a apela track(). |
| buget per vizualizare de pagină | Până la 60 de evenimente acceptate. Peste această limită, apelurile suplimentare sunt ignorate silențios pentru restul acelei vizualizări — protejând propriul tău raport de o buclă scăpată de sub control. Bugetul se resetează la fiecare vizualizare nouă de pagină, inclusiv la schimbări de rută în aplicații single-page. |
| identitate | Nu poartă niciodată identitatea first-party opt-in a vizitatorului și niciun câmp specific pageview-ului (referrer, adâncime de scroll, timp de lectură). Un eveniment personalizat e independent: un număr anonim al ce s-a întâmplat, nu o înregistrare a cine a făcut-o. |
| transport și răspuns | Trimis ca propriul tip de eveniment, pe exact același canal ca un pageview — întâi sendBeacon, apoi fetch cu keepalive ca rezervă, fără preflight CORS. Serverul răspunde 202 la acceptare și 204 la respingere; codul tău nu vede niciodată vreunul dintre ele. |
Apelarea track() înainte ca scriptul să termine încărcarea
window.__afm există doar după ce scriptul (deferred) al trackerului s-a executat. Codul care ar putea rula mai devreme — un script inline aproape de începutul lui <head>, de exemplu — ar trebui să protejeze apelul, sau să lipească acest stub de o linie înaintea lui:
window.__afm = window.__afm || {
q: [],
track(n, p) {
if (this.q.length < 20) this.q.push([n, p]);
},
};
window.__afm.track("early-event", { source: "inline" });Trackerul real preia coada stub-ului chiar în momentul încărcării și livrează fiecare apel prin ea — nu adăuga o metodă optout() la stub, altfel cea reală nu s-ar mai instala peste ea.
Șase lucruri pe care redacțiile chiar le urmăresc
Copiezi, lipești, ajustezi selectorii. Fiecare rețetă folosește UN singur nume stabil de eveniment pentru toată acțiunea — vezi „Denumirea corectă a evenimentelor” mai jos, ca să înțelegi de ce contează.
Interacțiune cu videoclipuri
Afli ce videoclipuri chiar rețin atenția, nu doar ce pagini au un player.
const video = document.querySelector("video");
const firedAt = new Set();
function maybeFire(pct) {
if (firedAt.has(pct)) return;
firedAt.add(pct);
window.__afm.track("video-played", {
quartile: String(pct),
title: video.dataset.title,
});
}
video.addEventListener("timeupdate", () => {
const pct = Math.floor((video.currentTime / video.duration) * 100);
if (pct >= 25) maybeFire(25);
if (pct >= 50) maybeFire(50);
if (pct >= 75) maybeFire(75);
});
video.addEventListener("ended", () => maybeFire(100));Conversii: înscrieri și click-uri pe paywall
Atribui înscrierile la newsletter și click-urile pe paywall articolului și plasării care le-a generat.
document.querySelector("#newsletter-form")
.addEventListener("submit", () => {
window.__afm.track("newsletter-signup", { placement: "in-article" });
});
document.querySelectorAll(".paywall-cta").forEach((el) => {
el.addEventListener("click", () => {
window.__afm.track("paywall-hit", { plan: el.dataset.plan });
});
});Sondaje și chestionare
Vezi la ce întrebări chiar răspund cititorii, și cum — nu doar că o pagină de chestionar a avut trafic.
function onQuizAnswered(question, answer) {
window.__afm.track("quiz-answered", { question, answer });
}
function onPollVoted(poll, option) {
window.__afm.track("poll-voted", { poll, option });
}Linkuri externe și afiliate
Măsori click-urile către parteneri și oferte afiliate fără să ieși din propriul tău analytics.
document.querySelectorAll("a[data-affiliate]").forEach((a) => {
a.addEventListener("click", () => {
window.__afm.track("affiliate-click", { merchant: a.dataset.affiliate });
});
});
document.querySelectorAll("a[data-outbound]").forEach((a) => {
a.addEventListener("click", () => {
window.__afm.track("outbound-click", {
domain: new URL(a.href).hostname,
});
});
});Căutare pe site
Afli ce caută cititorii și navigarea ta nu scoate deja la suprafață.
searchForm.addEventListener("submit", () => {
// Trim it yourself: an oversized value is DROPPED whole (never truncated).
window.__afm.track("site-search", {
query: input.value.trim().slice(0, 80),
results: String(resultCount),
});
});Jocuri și conținut interactiv
Urmărești interacțiunea în jocuri încorporate, calculatoare sau grafice interactive.
function onGameOver(score, level, won) {
// Numbers and booleans are stringified for you — no String() needed.
window.__afm.track("game-played", { score, level, won });
}Denumirea corectă a evenimentelor
Numele evenimentului este dimensiunea după care se grupează orice tablou, grafic și CSV — îl faci bine o singură dată și rămâne un clasament curat pentru totdeauna.
- Un singur nume stabil per ACȚIUNE, nu per instanță. video-played pentru fiecare videoclip de pe site, nu câte un nume nou per titlu — videoclipul în sine e o proprietate, nu parte din nume.
- Minuscul și cu cratimă, exact cum îl normalizează oricum trackerul: newsletter-signup, nu Newsletter_Signup sau signedUpForNewsletter.
- Pui partea variabilă în props, nu în nume: quiz-answered cu { question, answer }, niciodată quiz-answered-question-3.
- Refolosești același nume pe fiecare pagină unde se aplică — exact această repetiție transformă beacon-urile individuale într-un raport clasat, nu într-un rând per pagină.
În practică
video-played
Un singur nume pentru fiecare videoclip de pe site. { quartile, title } duce partea care variază.
play-titanic-25pct
Un nume nou per videoclip fragmentează raportul în sute de rânduri izolate, care nu se agregă niciodată în nimic.
newsletter-signup
Refolosit pe fiecare articol, bară laterală și formular din footer. { placement } le deosebește.
footer-form-submitted-2026
Bagă plasarea și anul direct în nume — anul viitor va avea nevoie de un nume nou, care nu se va compara cu acesta.
Citirea datelor
Fiecare eveniment acceptat apare automat în două locuri — nimic altceva de configurat.
Realtime → cardul Events
Top 8 nume de evenimente din ultimele 30 de minute, fiecare cu un număr de apariții și un număr de vizitatori unici. Se reîmprospătează în același ritm ca restul tabloului realtime.
Raportul Events
Un istoric complet: un tablou clasat, sortabil și căutabil (nume, apariții, vizitatori, cotă din interval) și un grafic cu trend cumulat pentru top 8 nume, pe orice interval de date, cu export CSV. Evenimentele personalizate păstrează 13 luni de istoric — mai mult decât fereastra de 90 de zile folosită de majoritatea rapoartelor pe evenimente brute, pentru că în spatele lor nu există nicio agregare precalculată.
Ascunderea unui nume
Un administrator al site-ului poate ascunde un nume de eveniment din orice raport, tablou și CSV, din Settings → Custom events — util pentru un nume de test, un bug, sau orice n-ai vrea să arăți unui coleg. Ascunderea nu oprește niciodată colectarea și nu șterge nimic: dezactivezi ascunderea numelui și tot istoricul reapare, de regulă în circa un minut.
Deocamdată nu e pe API-ul public de citire — raportul Events e doar în dashboard, în timp ce orice alt raport istoric (afișări, surse, autori, categorii…) se poate citi deja cu cheia site-ului tău. Vezi referința API-ului public din documentația principală. Documentație →
Limite și confidențialitate, într-un singur tabel
Două dintre acestea sunt limite, nu funcții — bine de știut înainte de primul raport de bug despre un eveniment „dispărut”.
| Limită | Valoare |
|---|---|
| Numele evenimentului | Minuscul a–z, 0–9, - _ . : (trebuie să înceapă cu o literă sau o cifră); cel mult 64 de octeți. |
| Props per eveniment | Cel mult 10 intrări; ce depășește e ignorat, nu combinat. |
| Cheie prop | Același set de caractere ca numele evenimentului; cel mult 32 de octeți. |
| Valoare prop | Cel mult 200 de octeți UTF-8 odată transformată în text; valorile prea mari sunt ignorate complet, niciodată trunchiate. |
| Evenimente per vizualizare de pagină | 60 acceptate; ce depășește e ignorat silențios. Se resetează la fiecare vizualizare nouă de pagină, inclusiv la schimbări de rută în aplicații single-page. |
| Coadă de pre-inițializare | 20 de apeluri stocate până termină trackerul încărcarea. |
| Istoric păstrat | 13 luni (395 de zile) în raportul Events. |
Confidențialitate
Fără identitate atașată
Evenimentele personalizate nu poartă niciodată identitatea first-party opt-in a vizitatorului sau vreun alt câmp de identitate. Sunt numărători anonime ale ce s-a întâmplat, niciodată o înregistrare a cine a făcut-o.
Aceeași excludere ca orice altceva
Global Privacy Control și window.__afm.optout() reduc la tăcere evenimentele personalizate exact ca pageview-urile — un cititor exclus nu trimite absolut nimic, niciodată.
Validarea pe server e plasa de siguranță
Trackerul curăță totul înainte să trimită vreun octet; serviciul de colectare revalidează independent și respinge silențios orice trimite greșit un expeditor scris de mână, deci o integrare terță ruptă nu poate corupe niciodată raportul cu date pe jumătate valide.
Ce întreabă lumea, de fapt
Trebuie să configurez ceva înainte să apelez track()?
Nu. Dacă scriptul trackerului e instalat, window.__afm.track(...) funcționează imediat — nu există comutator per site, script separat sau pas de dashboard de completat înainte.
Ce se întâmplă dacă apelez track() cu un nume invalid?
Evenimentul e ignorat silențios: nu se trimite nimic, iar codul tău nu vede nicio eroare. Dacă un eveniment nu apare, verifică numele față de ^[a-z0-9][a-z0-9_.:-]{0,63}$ — minuscul, fără spații sau punctuație în afară de - _ . :
Pot atașa identitatea vizitatorului la un eveniment personalizat?
Nu, intenționat. Evenimentele personalizate nu poartă niciodată identitatea first-party opt-in sau vreun alt câmp de identitate — sunt numărători anonime ale ce s-a întâmplat, nu înregistrări ale cine a făcut-o.
O avalanșă de apeluri track() îmi afectează datele de pageview?
Nu. Evenimentele personalizate sunt validate, limitate ca rată și stocate complet separat de pageview-uri, ping-uri și exit-uri — o buclă scăpată de sub control își poate epuiza doar propriul buget de 60 de evenimente per vizualizare, niciodată nu se revarsă în numărătoarea de pageview-uri.
Pot citi evenimentele personalizate prin API-ul public?
Deocamdată nu. Raportul Events e doar în dashboard; orice alt raport istoric e deja disponibil prin API-ul public de citire, cu cheia site-ului tău.
Am ascuns din greșeală un nume de eveniment — datele s-au pierdut?
Nu. Ascunderea afectează doar afișarea. Dezactivezi ascunderea numelui din Settings → Custom events și tot istoricul reapare, de regulă în circa un minut.
Vrei o cheie cu care să încerci?
Accesul timpuriu este momentan pe bază de invitație. Cere acces și îți punem tabloul pe direct.