Evenimente personalizate

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.

Start rapid

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.

Același script, o linie în plus
<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.

Referință

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.

Semnătură
window.__afm.track(
  name: string,
  props?: Record<string, string | number | boolean>
): void
API-ul track()
RegulăCe înseamnă
nameobligatoriuMinuscul, 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.
propsPerechi 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 propAcelaș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 propCel 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.
identitateNu 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ăspunsTrimis 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:

Stub opțional de pre-inițializare
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.

Rețete

Ș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.

Interacțiune cu videoclipuri
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.

Conversii: înscrieri și click-uri pe paywall
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.

Sondaje și chestionare
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.

Linkuri externe și afiliate
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ță.

Căutare pe site
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.

Jocuri și conținut interactiv
function onGameOver(score, level, won) {
  // Numbers and booleans are stringified for you — no String() needed.
  window.__afm.track("game-played", { score, level, won });
}
Bună practică

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.

Unde apar

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

Referință

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”.

Limite și confidențialitate, într-un singur tabel
LimităValoare
Numele evenimentuluiMinuscul a–z, 0–9, - _ . : (trebuie să înceapă cu o literă sau o cifră); cel mult 64 de octeți.
Props per evenimentCel mult 10 intrări; ce depășește e ignorat, nu combinat.
Cheie propAcelași set de caractere ca numele evenimentului; cel mult 32 de octeți.
Valoare propCel 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țializare20 de apeluri stocate până termină trackerul încărcarea.
Istoric păstrat13 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.

Întrebări frecvente

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.