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-loadedcon il suoavacy: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, usawindow.Avacy.getData()?.core?.ready(snapshot sincrono) invece di aspettare l'evento.
Eventi disponibili
| Event name | Fires when | detail shape |
|---|---|---|
avacy:ready | Core ready + banner (eventualmente) mountato | null |
avacy:consent-saved | L'utente (o l'API) ha fatto una scelta | { type, timestamp, gcm } |
avacy:consent-loaded | Il CMP ha letto il consenso salvato (boot e reload()) | { valid, type?, timestamp?, gcm? } |
avacy:consent-updated | Un campo del record è cambiato senza una nuova scelta | { fields, ...campi } |
avacy:consent-changed | Qualunque cambio del consenso, con il motivo | { cause, valid, ... } |
avacy:banner-shown | Banner first-layer mountato | { layer: 'first' } |
avacy:banner-hidden | Banner first-layer unmountato, oppure reload() | { reason: 'consent' | 'destroy' } |
avacy:cpc-opened | CPC (second-layer) aperto | null |
avacy:cpc-closed | CPC chiuso | null |
avacy:language-changed | Lingua attiva cambiata | { lang, prev } |
avacy:log | Riga 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.
cause | Quando | Evento specifico |
|---|---|---|
decision | Una scelta: banner, CPC, acceptAll/rejectAll/triggerConsent/updateConsent | avacy:consent-saved |
load | Lettura del consenso salvato, al boot e a ogni reload() | avacy:consent-loaded |
record-update | Un 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: unPersonalizedle cui scelte coincidono con un accetta/rifiuta tutto arriva comeAcceptAll/RejectAll(vedi Consent actions).gcmcontiene i 7 stati Google Consent Mode, gli stessi che il CMP spinge neldataLayercongtag('consent', 'update', …). Èundefinedquando il webspace non ha GCM inframeworks: "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 chiamatoreload(). 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'arrayloggerscon['console']: finché il flag è attivo,avacy:lognon parte.
Pattern d'uso comuni
Reagire agli stati Google Consent Mode
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__tcfapipuò 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 aspettaretcloadedouseractioncomplete.
Al caricamento della pagina
avacy_consent_updatearriva prima diavacy:consent-loaded: in quel momentoAvacy.getConsent()risponde ancoraready: false, senza scelte. Se un tag deve decidere congetConsent(), fallo partire daavacy: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.