Passa al contenuto principale

Events

Il CMP emette eventi del DOM sul document (CustomEvent). Compatibili con qualunque tag manager, script analytics, RUM tool, bridge nativo.

document.addEventListener('avacy:consent-saved', (e) => {
console.log(e.detail);
});

Niente import necessari, niente ready-pattern: gli eventi continuativi (avacy:consent-saved, avacy:consent-updated, avacy:consent-changed, avacy:cpc-*, avacy:language-changed, avacy:banner-hidden) sono fired on demand, quindi un listener registrato in qualunque momento riceve quelli successivi.

⚠️ Gli eventi one-shot di boot (avacy:consent-loaded con il suo avacy:consent-changed { cause: 'load' }, avacy:ready, avacy:banner-shown { layer: 'first' }) vengono fired una volta per init. Un listener registrato dopo quel momento li perde. Se devi reagire a "il CMP è pronto" tardi nel flow, usa window.Avacy.getData()?.core?.ready (snapshot sincrono) invece di aspettare l'evento.

Eventi disponibili​

Event nameFires whendetail shape
avacy:readyCore ready + banner (eventualmente) mountatonull
avacy:consent-savedL'utente (o l'API) ha fatto una scelta{ type, timestamp, gcm }
avacy:consent-loadedIl CMP ha letto il consenso salvato (boot e reload()){ valid, type?, timestamp?, gcm? }
avacy:consent-updatedUn campo del record è cambiato senza una nuova scelta{ fields, ...campi }
avacy:consent-changedQualunque cambio del consenso, con il motivo{ cause, valid, ... }
avacy:banner-shownBanner first-layer mountato{ layer: 'first' }
avacy:banner-hiddenBanner first-layer unmountato, oppure reload(){ reason: 'consent' | 'destroy' }
avacy:cpc-openedCPC (second-layer) apertonull
avacy:cpc-closedCPC chiusonull
avacy:language-changedLingua attiva cambiata{ lang, prev }
avacy:logRiga di log del core, solo con loggers: ['event']{ level, message, topic }

avacy:ready​

detail: null

Emesso una volta, dopo che l'init del CMP ha completato e l'UI è (eventualmente) mountata. Rie-emesso dopo ogni reload().

document.addEventListener('avacy:ready', () => {
console.log('CMP ready', window.Avacy.version);
});

Catalogo degli eventi del consenso​

Ogni scrittura del consenso porta un motivo (cause). Il motivo dà il nome all'evento. La tabella vive in un solo posto, packages/cmp-web/src/public-events.ts (CONSENT_EVENT_BY_CAUSE), e vale anche per gli add-on.

causeQuandoEvento specifico
decisionUna scelta: banner, CPC, acceptAll/rejectAll/triggerConsent/updateConsentavacy:consent-saved
loadLettura del consenso salvato, al boot e a ogni reload()avacy:consent-loaded
record-updateUn campo del record cambia senza una nuova scelta (es. l'id dell'archivio)avacy:consent-updated

Ogni cambio emette anche l'ombrello avacy:consent-changed, dopo quello specifico, con detail.cause.

Gli altri motivi del core (boot, language, customization) non toccano il consenso. Chi ascolta CoreEvents li trova in meta.cause (vedi Tier 2).


avacy:consent-saved​

detail: {
type: 'AcceptAll' | 'RejectAll' | 'Personalized';
timestamp: number; // ms epoch
}

type GcmConsentType =
| 'ad_storage' | 'ad_user_data' | 'ad_personalization' | 'analytics_storage'
| 'functionality_storage' | 'personalization_storage' | 'security_storage';

Emesso quando c'è una scelta (cause: 'decision'): banner click, CPC save, acceptAll / rejectAll / updateConsent / triggerConsent API. triggerConsent({ type: 'CloseBanner' }) non scrive niente e non lo emette. Non parte al boot né a reload(): lì il consenso viene solo riletto, e parte avacy:consent-loaded.

  • type è il tipo salvato: un Personalized le cui scelte coincidono con un accetta/rifiuta tutto arriva come AcceptAll/RejectAll (vedi Consent actions).
  • gcm contiene i 7 stati Google Consent Mode, gli stessi che il CMP spinge nel dataLayer con gtag('consent', 'update', …). È undefined quando il webspace non ha GCM in frameworks: "non configurato" non è "tutto negato".
document.addEventListener('avacy:consent-saved', (e) => {
console.log('user saved:', e.detail.type, 'at', new Date(e.detail.timestamp));

// Per accedere alle choices dettagliate:
const consent = window.Avacy.getConsent();
console.log(consent.choices);
});

💡 Il detail resta plain JSON: tipo, ora e stati GCM, non le scelte per fornitore o finalità. Per quelle chiama getConsent() dentro il handler.


avacy:consent-loaded​

detail:
| { valid: true; type: 'AcceptAll' | 'RejectAll' | 'CloseBanner' | 'Personalized'; timestamp: number; gcm?: Record<GcmConsentType, 'granted' | 'denied'> }
| { valid: false }; // nessun consenso utile: assente, scaduto, policy superata

Emesso una volta a ogni boot e a ogni reload(), quando il CMP ha letto il consenso salvato. Arriva prima di avacy:ready, quando la UI non è ancora montata: per riceverlo al primo boot registra il listener prima che il bundle parta, e per agire sul consenso (es. acceptAll()) aspetta avacy:ready.

document.addEventListener('avacy:consent-loaded', (e) => {
if (e.detail.valid) applyConsent(e.detail.gcm);
});

avacy:consent-updated​

detail: { fields: string[]; [field: string]: unknown }

Emesso quando un campo del record del consenso cambia senza una nuova scelta (cause: 'record-update'). Le scelte restano le stesse: GCM, TCF e l'archivio non ripartono. fields elenca i campi toccati, ognuno è nel detail con il suo valore nuovo.


avacy:consent-changed​

detail: {
cause?: 'decision' | 'load' | 'record-update'; // assente se chi scrive non l'ha dichiarato
valid: boolean;
type?: string;
timestamp?: number;
gcm?: Record<GcmConsentType, 'granted' | 'denied'>;
fields?: string[]; // solo per 'record-update', con i campi toccati
}

L'ombrello: parte per ogni cambio del consenso, subito dopo l'evento specifico. Comodo per chi vuole un solo listener e decidere da detail.cause.


avacy:banner-shown​

detail: { layer: 'first' }

Emesso quando l'avacy-banner (first-layer) viene mountato. Tipicamente: al boot (se nessun consenso valido) e dopo reload().

document.addEventListener('avacy:banner-shown', () => {
pushToDataLayer({ event: 'cmp_shown' });
});

avacy:banner-hidden​

detail: { reason: 'consent' | 'destroy' }
  • reason: 'consent' — è stato salvato un consenso valido mentre il banner era montato: il banner viene rimosso (e al suo posto compare lo shield).
  • reason: 'destroy' — è stato chiamato reload(). Parte sempre, anche se in pagina non c'era nessun banner ma solo lo shield: non usarlo per contare i banner chiusi.
document.addEventListener('avacy:banner-hidden', (e) => {
if (e.detail.reason === 'consent') pushToDataLayer({ event: 'cmp_dismissed' });
});

avacy:cpc-opened​

detail: null

Emesso quando il Consent Preference Center (second-layer) viene aperto, indipendentemente dal call site (click banner, click shield, chiamata API).

document.addEventListener('avacy:cpc-opened', () => {
pushToDataLayer({ event: 'cmp_cpc_open' });
});

avacy:cpc-closed​

detail: null

Speculare a cpc-opened: emesso alla chiusura del CPC.


avacy:language-changed​

detail: { lang: string; prev: string }

Emesso dopo che la lingua attiva è cambiata (autoLanguage detection o changeLanguage(lang) API). Non parte quando un reload() rilegge la lingua configurata. La lingua rilevata da autoLanguage lo emette dopo un reload(), non al primo boot (il listener si aggancia dopo la rilevazione).

document.addEventListener('avacy:language-changed', (e) => {
console.log(`lang ${e.detail.prev} → ${e.detail.lang}`);
});

avacy:log​

detail: {
level: 'info' | 'warn' | 'error';
message: string;
topic?: string; // es. 'gcm', 'tcf', 'core', 'addons:consent-archive'
}

Una riga di log del core come evento DOM, invece che in console. Parte solo se la config attiva il logger event:

{ "loggers": ["event"] }

loggers è un array: ["console", "event"] li accende entrambi. Utile per mandare i log del CMP a un RUM tool o a un bridge nativo senza aprire la console.

document.addEventListener('avacy:log', (e) => {
if (e.detail.level === 'error') reportToRum('avacy', e.detail);
});

setDebug(true) sostituisce l'array loggers con ['console']: finché il flag è attivo, avacy:log non parte.


Pattern d'uso comuni​

Se il webspace ha GCM in frameworks, il loader chiama già da sé gtag('consent', 'default', …) (tutto denied) e il core gtag('consent', 'update', …) a ogni consenso: non serve ripeterlo. Subito dopo l'update il core spinge anche { event: 'avacy_consent_update' } nel dataLayer, come il legacy: è il trigger GTM per i tag che aspettano il consenso. Fa eccezione la prima visita in mapping mode (mapToPurpose): lì parte l'update denied, ma non l'evento, perché il visitatore non ha ancora scelto. e.detail.gcm serve per la logica tua, con gli stessi valori:

document.addEventListener('avacy:consent-saved', (e) => {
const gcm = e.detail.gcm;
if (!gcm) return; // GCM non configurato nel webspace

if (gcm.analytics_storage === 'granted') initAnalytics();
window.dataLayer?.push({ event: 'avacy_consent_saved', consent_type: e.detail.type });
});

Se il webspace ha anche il TCF, all'arrivo di avacy_consent_update __tcfapi può avere ancora il consenso precedente: dipende dall'ordine dei framework nella config. Un tag che legge il TCF non deve partire da questo evento, ma ascoltare __tcfapi('addEventListener', …) e aspettare tcloaded o useractioncomplete.

Al caricamento della pagina avacy_consent_update arriva prima di avacy:consent-loaded: in quel momento Avacy.getConsent() risponde ancora ready: false, senza scelte. Se un tag deve decidere con getConsent(), fallo partire da avacy:consent-loaded. Dopo una scelta del visitatore, invece, getConsent() è già aggiornato quando arriva l'evento.

Bridge per WebView nativa​

document.addEventListener('avacy:consent-saved', (e) => {
if (window.Android) {
window.Android.sendConsent(JSON.stringify(e.detail));
} else if (window.webkit?.messageHandlers?.CMPWebInterface) {
window.webkit.messageHandlers.CMPWebInterface.postMessage({
command: 'consent',
values: JSON.stringify(e.detail),
});
}
});

💡 Per casi enterprise come questo, considera invece un build variant con uno storage provider custom — vedi Tier 2 advanced.