Guida al consenso sui cookie e all'implementazione di SPA
Comprendere la sfida
Le applicazioni a pagina singola (SPA) realizzate con React, Vue, Angular o altri framework presentano sfide uniche per il consenso ai cookie di TermsFeed perché:
- Nessun aggiornamento della pagina. La pagina non si ricarica come le pagine web classiche.
- Contenuto dinamico. Il contenuto viene caricato dinamicamente senza attivare nuovamente DOMContentLoaded.
- Cambiamenti di percorso. La navigazione avviene tramite JavaScript, non tramite i tradizionali caricamenti di pagina.
- Gestione dello stato. Lo stato del consenso deve persistere nonostante i cambiamenti di percorso.
Soluzione: ricaricare durante la navigazione
Problema
"Ho una SPA React e il banner dei cookie viene visualizzato solo al primo caricamento della pagina. Quando gli utenti navigano verso altre pagine, il banner non appare più se non hanno effettuato una scelta. Inoltre, se accettano i cookie su una pagina, gli script non vengono caricati sulle pagine successive fino a un aggiornamento completo."
Soluzione
Quando si implementa TermsFeed Cookie Consent in una SPA, è necessario:
- Inizializzare il consenso al primo caricamento.
- Rilevare i cambiamenti di percorso.
- Ricaricare il banner se necessario.
- Verificare nuovamente lo stato del consenso.
Implementazione in React
Passaggio 1: Creare un componente Cookie Consent
// CookieConsent.js
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
const CookieConsent = () => {
const location = useLocation();
useEffect(() => {
// Load TermsFeed script if not already loaded
if (!window.cookieconsent) {
const script = document.createElement('script');
script.src = '//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js';
script.charset = 'UTF-8';
script.onload = () => initializeCookieConsent();
document.head.appendChild(script);
} else {
// Script already loaded, just check if banner needs to be shown
checkBannerState();
}
}, []); // Run once on mount
useEffect(() => {
// Check banner state on route change
if (window.cookieconsent) {
checkBannerState();
}
}, [location]); // Run on every route change
const initializeCookieConsent = () => {
if (window.cookieconsent && typeof window.cookieconsent.run === 'function') {
window.cookieconsent.run({
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "light",
"language": "en",
"website_name": "My SPA",
"website_privacy_policy_url": "https://mysite.com/privacy",
"open_preferences_center_selector": "#changePreferences",
"page_load_consent_levels": ["strictly-necessary"],
"callbacks": {
"scripts_specific_loaded": (level) => {
console.log("Scripts loaded for: " + level);
// Re-initialize any components that need tracking
if (level === 'tracking') {
reinitializeAnalytics();
}
}
}
});
}
};
const checkBannerState = () => {
// Check if user has already made a consent choice
const consentCookie = getCookie('cookie_consent_level');
if (!consentCookie) {
// No consent yet, banner should be visible
// No action needed - banner will show automatically
}
};
const getCookie = (name) => {
const value = ; ${document.cookie};
const parts = value.split(; ${name}=);
if (parts.length === 2) return parts.pop().split(';').shift();
};
const reinitializeAnalytics = () => {
// Re-run any analytics initialization needed
if (window.gtag) {
window.gtag('config', 'G-XXXXXXXXXX', {
'page_path': location.pathname
});
}
};
return null; // This component doesn't render anything
};
export default CookieConsent;
Passaggio 2: Aggiungere al componente App
// App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import CookieConsent from './components/CookieConsent';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<Router>
<CookieConsent />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
{/ Preferences button /}
<button id="changePreferences" style={{
position: 'fixed',
bottom: '20px',
right: '20px',
zIndex: 9999
}}>
Cookie Settings
</button>
<noscript>
Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed</a>
</noscript>
</Router>
);
}
export default App;
Implementazione Vue.js
Passaggio 1: Creare il plugin Cookie Consent
// plugins/cookieConsent.js
export default {
install(app, options) {
// Load script
const loadCookieConsent = () => {
if (window.cookieconsent) return Promise.resolve();
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = '//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js';
script.charset = 'UTF-8';
script.onload = () => resolve();
script.onerror = () => reject();
document.head.appendChild(script);
});
};
// Initialize
const initCookieConsent = () => {
if (window.cookieconsent && typeof window.cookieconsent.run === 'function') {
window.cookieconsent.run({
"notice_banner_type": "headline",
"consent_type": "express",
"palette": "light",
"language": "en",
"website_name": options.websiteName || "My Vue SPA",
"website_privacy_policy_url": options.privacyUrl || "#",
"open_preferences_center_selector": "#changePreferences",
"page_load_consent_levels": ["strictly-necessary"]
});
}
};
// Make available globally
app.config.globalProperties.$cookieConsent = {
load: loadCookieConsent,
init: initCookieConsent
};
// Auto-initialize on mount
if (typeof window !== 'undefined') {
loadCookieConsent().then(() => {
document.addEventListener('DOMContentLoaded', initCookieConsent);
});
}
}
};
Passaggio 2: Utilizzarlo nell'app principale
// main.js
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import cookieConsentPlugin from './plugins/cookieConsent';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: () => import('./views/Home.vue') },
{ path: '/about', component: () => import('./views/About.vue') }
]
});
const app = createApp(App);
app.use(router);
app.use(cookieConsentPlugin, {
websiteName: 'My Vue App',
privacyUrl: 'https://mysite.com/privacy'
});
app.mount('#app');
Passaggio 3: Creare il componente App
<!-- App.vue -->
<template>
<div id="app">
<router-view />
<button id="changePreferences" class="cookie-settings-btn">
Cookie Settings
</button>
<noscript>
Free cookie consent management tool by
<a href="https://www.termsfeed.com/">TermsFeed</a>
</noscript>
</div>
</template>
<script>
export default {
name: 'App',
mounted() {
// Watch for route changes
this.$router.afterEach((to, from) => {
// Check if consent banner needs to be re-shown
this.checkConsentState();
});
},
methods: {
checkConsentState() {
const consentCookie = document.cookie
.split('; ')
.find(row => row.startsWith('cookie_consent_level='));
if (!consentCookie) {
console.log('No consent given yet');
}
}
}
};
</script>
<style>
.cookie-settings-btn {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
Implementazione Angular
Passaggio 1: Creare il servizio Cookie Consent
// services/cookie-consent.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
declare global {
interface Window {
cookieconsent: any;
}
}
@Injectable({
providedIn: 'root'
})
export class CookieConsentService {
private scriptLoaded = false;
constructor(private router: Router) {}
initialize(): Promise<void> {
return this.loadScript().then(() => {
this.initializeConsent();
this.watchRouteChanges();
});
}
private loadScript(): Promise<void> {
if (this.scriptLoaded || window.cookieconsent) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = '//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js';
script.charset = 'UTF-8';
script.onload = () => {
this.scriptLoaded = true;
resolve();
};
script.onerror = () => reject();
document.head.appendChild(script);
});
}
private initializeConsent(): void {
if (window.cookieconsent && typeof window.cookieconsent.run === 'function') {
window.cookieconsent.run({
notice_banner_type: 'headline',
consent_type: 'express',
palette: 'light',
language: 'en',
website_name: 'My Angular App',
website_privacy_policy_url: 'https://mysite.com/privacy',
open_preferences_center_selector: '#changePreferences',
page_load_consent_levels: ['strictly-necessary'],
callbacks: {
scripts_specific_loaded: (level: string) => {
console.log('Scripts loaded for:', level);
this.handleConsentUpdate(level);
}
}
});
}
}
private watchRouteChanges(): void {
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe(() => {
// Check consent state on route change
this.checkConsentState();
});
}
private checkConsentState(): void {
const consentCookie = this.getCookie('cookie_consent_level');
if (!consentCookie) {
console.log('No consent given yet');
}
}
private handleConsentUpdate(level: string): void {
// Handle consent updates
if (level === 'tracking') {
this.initializeAnalytics();
}
}
private initializeAnalytics(): void {
// Re-initialize analytics on consent
if ((window as any).gtag) {
(window as any).gtag('config', 'G-XXXXXXXXXX', {
page_path: this.router.url
});
}
}
private getCookie(name: string): string | null {
const value = ; ${document.cookie};
const parts = value.split(; ${name}=);
if (parts.length === 2) {
return parts.pop()?.split(';').shift() || null;
}
return null;
}
openPreferences(): void {
if (window.cookieconsent && typeof window.cookieconsent.openPreferencesCenter === 'function') {
window.cookieconsent.openPreferencesCenter();
}
}
}
Passaggio 2: Inizializzare nel componente App
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { CookieConsentService } from './services/cookie-consent.service';
@Component({
selector: 'app-root',
template:
<router-outlet></router-outlet>
<button id="changePreferences" class="cookie-settings-btn">
Cookie Settings
</button>
<noscript>
Free cookie consent management tool by
<a href="https://www.termsfeed.com/">TermsFeed</a>
</noscript>
,
styles: [
.cookie-settings-btn {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
]
})
export class AppComponent implements OnInit {
constructor(private cookieConsentService: CookieConsentService) {}
ngOnInit(): void {
this.cookieConsentService.initialize().catch(error => {
console.error('Failed to initialize cookie consent:', error);
});
}
}
Problemi comuni relativi alle SPA e al consenso ai cookie
Problema 1: il banner non ricompare durante la navigazione
Problema
L'utente naviga senza effettuare alcuna scelta, il banner scompare.
Soluzione
Verificare se il consenso è stato dato al cambio di percorso.
// Check if consent was given on route change
router.afterEach(() => {
const hasConsent = getCookie('cookie_consent_level');
if (!hasConsent) {
// Banner should be visible - it will show automatically
// No manual action needed if properly configured
}
});
Problema 2: gli script non vengono caricati dopo il consenso su nuovi percorsi
Problema
"L'utente accetta i cookie, naviga su un'altra pagina nella mia app React, ma Google Analytics non rileva la visualizzazione della nuova pagina."
Soluzione
Utilizza i callback per tracciare la visualizzazione della pagina ad ogni cambio di percorso.
// In your callback
"callbacks": {
"scripts_specific_loaded": (level) => {
if (level === 'tracking' && window.gtag) {
// Track page view on every route change
router.afterEach((to) => {
gtag('config', 'G-XXXXXXXXXX', {
'page_path': to.path
});
});
}
}
}
Problema 3: Vengono visualizzate più istanze del banner
Problema
Il banner appare più volte quando si naviga rapidamente.
Soluzione
Utilizza un guard per impedire inizializzazioni multiple
// Use a guard to prevent multiple initializations
let isInitialized = false;
const initCookieConsent = () => {
if (isInitialized) return;
isInitialized = true;
cookieconsent.run({ / config / });
};
Aprire il Centro preferenze a livello di programmazione
Per le SPA, potresti voler aprire le preferenze senza un pulsante statico:
// Method 1: Using the openPreferencesCenter() method
const openPreferences = () => {
if (window.cookieconsent && typeof window.cookieconsent.openPreferencesCenter === 'function') {
window.cookieconsent.openPreferencesCenter();
} else {
console.error('Cookie consent not initialized');
}
};
// In React
<button onClick={openPreferences}>Change Cookie Settings</button>
// In Vue
<button @click="openPreferences">Change Cookie Settings</button>
// In Angular
<button (click)="openPreferences()">Change Cookie Settings</button>