Skrypt tworzący na stronie banner consent mode

Strona główna » Skrypty » Skrypt tworzący na stronie banner consent mode
Instrukcja użycia

→ Wklej poniższy skrypt w sekcji <head> na stronie internetowej
→ W GTM skorzystaj z szablonu Consent Mode (gtm-templates-simo-ahava)
→ Użyj poniższych zmiennych, aby przechwycić status zgód
→ Dostosuj tag oraz zgody Consent Mode do ustawień w GYM

Szczegółową instrukcję znajdziesz tutaj.

Skrypt bannera

<style id="cb-styles">
  /* ===================== STYLE (akcent: #1960BF) ===================== */
  :root{ --cb-ico-scale:1.30; } /* powiększanie samej ikonki ciasteczka */

  #cb-overlay{
    position:fixed; inset:0; background:rgba(0,0,0,.45);
    z-index:2147483647;
    display:flex; align-items:center; justify-content:center;
  }
  #cb-modal{
    background:#ffffff; color:#0f172a; width:min(720px,92vw);
    border-radius:16px; border:1px solid #e5e7eb; box-shadow:0 24px 80px rgba(0,0,0,.25);
    overflow:hidden; z-index:2147483647;
  }
  .cb-top{ padding:0 24px; border-bottom:1px solid #e5e7eb; display:flex; gap:28px; }
  .cb-tab{ padding:16px 0 14px; border-bottom:3px solid transparent; cursor:pointer; font-weight:800; color:#0f172a; }
  .cb-tab.active{ border-color:#1960BF; }

  .cb-body{ padding:22px; max-height:60vh; overflow:auto; }
  .cb-title{ margin:0 0 10px; font-size:28px; font-weight:800; }
  .cb-desc{ margin:0 0 16px; color:#475569; font-size:16px; }
  .cb-info{ font-size:16px; color:#0f172a; }
  .cb-info a{ color:#1960BF; font-weight:800; text-decoration:none; border-bottom:2px solid #1960BF; padding-bottom:2px; }

  .cb-list{ margin-top:8px; border-top:1px solid #e5e7eb; }
  .cb-li{
    display:flex; align-items:flex-start; justify-content:space-between;
    padding:16px 0; border-bottom:1px solid #e5e7eb; gap:16px;
  }
  .cb-li strong{ font-weight:800; }
  .cb-left{ min-width:0; }
  .cb-sub{ margin:.35rem 0 0; color:#64748b; font-size:14px; line-height:1.4; }
  .cb-right{ display:flex; align-items:center; gap:12px; flex:0 0 auto; }

  /* Suwak */
  .cb-switch{ position:relative; width:56px; height:32px; }
  .cb-switch input{ display:none; }
  .cb-slider{
    position:absolute; inset:0; background:#e7e7e7; border-radius:999px; transition:.2s;
  }
  .cb-slider:before{
    content:""; position:absolute; left:3px; top:3px; width:26px; height:26px;
    background:#fff; border-radius:50%; transition:.2s; box-shadow:0 1px 2px rgba(0,0,0,.25);
  }
  .cb-switch input:checked + .cb-slider{ background:#1960BF; }
  .cb-switch input:checked + .cb-slider:before{ transform:translateX(24px); }
  .cb-switch.disabled input:checked + .cb-slider{ background:#4A80D6; }
  .cb-switch.disabled{ opacity:.9; }

  .cb-actions{
    display:flex; gap:12px; flex-wrap:wrap; padding:16px 22px;
    border-top:1px solid #e5e7eb; background:#fafafa;
    margin-bottom:15px;
  }
  .cb-actions.center{ justify-content:center; }
  .cb-actions.end{ justify-content:flex-end; }

  .cb-btn{
    border:0; border-radius:12px; padding:12px 18px; cursor:pointer;
    font-weight:900; min-width:210px; text-align:center;
  }
  .cb-btn.primary{ background:#1960BF; color:#fff; }
  .cb-btn.secondary{ background:#1960BF; color:#fff; }
  .cb-btn.ghost{ background:#fff; border:1px solid #e5e7eb; color:#1960BF; }

  @media(max-width:640px){ .cb-btn{ flex:1 1 100%; min-width:100%; } }

  /* ===== LAUNCHER (ciasteczko) – stałe kółko, rośnie tylko ikona ===== */
  #custom-button{
    position:fixed; bottom:20px; left:20px; width:60px; height:60px; border:none; border-radius:50%;
    background-color:#1960BF; cursor:pointer; z-index:2147483647;
    display:flex; align-items:center; justify-content:center; transition:background-color .3s ease, transform .3s ease;
    overflow:hidden; /* klip ikony gdy będzie większa */
  }
  #custom-button .cb-ico-wrap{ width:100%; height:100%; display:flex; align-items:center; justify-content:center; }
  #custom-button .cb-ico{ width:36px; height:36px; display:block; transform:scale(var(--cb-ico-scale)); transform-origin:center; }
  #custom-button:hover{ background-color:#144a94; transform:scale(1.06); }
</style>

<script>
(function(){
  const COOKIE_NAME = "consent";
  const TTL_DAYS = 180;

  const UI = {
    text: {
      tabs:["Zgody","Szczegóły","Informacje"],
      title:"Używam plików cookie",
      desc:'Wykorzystuje pliki cookies do zapewnienia bezpieczeństwa, celów analitycznych i ulepszania naszej strony internetowej. Kliknij "Dostosuj", jeśli chcesz wybrać preferowane przez Ciebie pliki cookie.',
      detailsIntro:"Dostosuj, które kategorie plików cookie mają być włączone:",
      acceptAll:"Akceptuj wszystkie",
      rejectAll:"Odrzuć wszystkie",
      customize:"Dostosuj",
      save:"Akceptuj wybrane",
      infoHTML:'Więcej informacji znajdziesz w naszej <a href="/polityka-prywatnosci/" target="_blank" rel="nofollow">polityce prywatności</a>.'
    },
    detailsRows:[
      ["Funkcyjne (zawsze włączone)","functionality",true,"Pliki cookie konieczne do działania strony i nie przechowują danych pozwalających na identyfikację."],
      ["Analityczne","analytics",false,"Służą do zrozumienia zachowania użytkowników (np, liczba wizyt, źródła ruchu)."],
      ["Reklamowe","advertisement",false,"Służą do personalizacji reklam oraz analizy skuteczności kampanii."],
      ["Personalizacyjne","personalization",false,"Służą do dopasowania treści i interfejsu do użytkownika (np. zapamiętywanie preferencji, rekomendacje)."]
    ]
  };

  const KEYS = ["consent","functionality","analytics","advertisement","personalization"];

  const DEFAULTS = {
    consent:"no",
    functionality:"yes",
    analytics:"yes",
    advertisement:"yes",
    personalization:"yes"
  };

  function serializeState(state){
    return KEYS.map(k => `${k}:${state[k]}`).join(",");
  }

  function setCookieState(state, days=TTL_DAYS){
    const d = new Date(); d.setTime(d.getTime()+days*864e5);
    const secure = location.protocol==="https:"?"; Secure":"";
    document.cookie = `${COOKIE_NAME}=${serializeState(state)}; Expires=${d.toUTCString()}; Path=/; SameSite=Lax${secure}`;
  }
  function getCookieRaw(){
    const m = document.cookie.split(";").map(c=>c.trim()).find(c=>c.startsWith(COOKIE_NAME+"="));
    return m ? m.split("=",2)[1] : null;
  }
  function parseCookie(str){
    const s = {...DEFAULTS};
    if(!str) return s;
    str.split(",").forEach(p=>{
      const [k,v]=p.split(":");
      if(k&&v&&KEYS.includes(k)) s[k]=(v==="yes"?"yes":"no");
    });
    s.functionality="yes";
    return s;
  }
  const readState = () => parseCookie(getCookieRaw());
  const hasChoice = () => readState().consent==="yes";

  function pushDL(action, state){
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
      event: "button_consent_click",
      action,
      consent: {...state},
      consent_raw: serializeState(state),
      timestamp: new Date().toISOString()
    });
  }

  function build(initial){
    const ov=document.createElement("div");
    ov.id="cb-overlay";
    ov.innerHTML=`
      <div id="cb-modal">
        <div class="cb-top">
          <div class="cb-tab active" data-tab="consents">${UI.text.tabs[0]}</div>
          <div class="cb-tab" data-tab="details">${UI.text.tabs[1]}</div>
          <div class="cb-tab" data-tab="info">${UI.text.tabs[2]}</div>
        </div>
        <div class="cb-body">
          <div id="pane-consents">
            <h3 class="cb-title">${UI.text.title}</h3>
            <p class="cb-desc">${UI.text.desc}</p>
          </div>
          <div id="pane-details" style="display:none">
            <p class="cb-desc">${UI.text.detailsIntro}</p>
            <div class="cb-list">
              ${UI.detailsRows.map(([label,key,lock,desc])=>`
                <div class="cb-li">
                  <div class="cb-left">
                    <strong>${label}</strong>
                    ${desc?`<p class="cb-sub">${desc}</p>`:""}
                  </div>
                  <div class="cb-right">
                    <label class="cb-switch ${lock?"disabled":""}">
                      <input type="checkbox" id="cb_${key}" ${initial[key]==="yes"?"checked":""} ${lock?"disabled":""}>
                      <span class="cb-slider"></span>
                    </label>
                  </div>
                </div>`).join("")}
            </div>
          </div>
          <div id="pane-info" class="cb-info" style="display:none">${UI.text.infoHTML}</div>
        </div>
        <div class="cb-actions center" id="actions-consents">
          <button class="cb-btn ghost" id="btn-reject">${UI.text.rejectAll}</button>
          <button class="cb-btn ghost" id="btn-customize">${UI.text.customize}</button>
          <button class="cb-btn primary" id="btn-accept">${UI.text.acceptAll}</button>
        </div>
        <div class="cb-actions end" id="actions-details" style="display:none">
          <button class="cb-btn primary" id="btn-save">${UI.text.save}</button>
        </div>
      </div>`;
    document.body.appendChild(ov);

    const tabs=ov.querySelectorAll(".cb-tab");
    const panes={consents:ov.querySelector("#pane-consents"),details:ov.querySelector("#pane-details"),info:ov.querySelector("#pane-info")};
    const acts={consents:ov.querySelector("#actions-consents"),details:ov.querySelector("#actions-details")};

    function showTab(which){
      tabs.forEach(x=>x.classList.remove("active"));
      [...tabs].find(t=>t.dataset.tab===which).classList.add("active");
      Object.keys(panes).forEach(k=>panes[k].style.display = k===which ? "block" : "none");
      acts.consents.style.display = which==="consents" ? "flex":"none";
      acts.details.style.display  = which==="details"  ? "flex":"none";
    }
    tabs.forEach(t=>t.addEventListener("click",()=>showTab(t.dataset.tab)));

    ov.querySelector("#btn-accept").addEventListener("click",()=>{
      const s={consent:"yes",functionality:"yes",analytics:"yes",advertisement:"yes",personalization:"yes"};
      setCookieState(s);
      ov.remove();
      setTimeout(()=>pushDL("accept_all", s),500);
    });
    ov.querySelector("#btn-reject").addEventListener("click",()=>{
      const s={consent:"yes",functionality:"yes",analytics:"no",advertisement:"no",personalization:"no"};
      setCookieState(s);
      ov.remove();
      setTimeout(()=>pushDL("reject_all", s),500);
    });
    ov.querySelector("#btn-customize").addEventListener("click",()=>showTab("details"));
    ov.querySelector("#btn-save").addEventListener("click",()=>{
      const val=k=>(ov.querySelector("#cb_"+k)?.checked?"yes":"no");
      const s={
        consent:"yes",
        functionality:"yes",
        analytics:val("analytics"),
        advertisement:val("advertisement"),
        personalization:val("personalization")
      };
      setCookieState(s);
      ov.remove();
      setTimeout(()=>pushDL("accept_selected", s),500);
    });
  }

  function addLauncher(){
    if(document.getElementById("custom-button")) return;
    const btn=document.createElement("button");
    btn.id="custom-button"; btn.setAttribute("aria-label","Zmień zgody");

    /* to SAMO ciasteczko – tylko opakowane, by można było je skalować niezależnie od kółka */
    btn.innerHTML=`<span class="cb-ico-wrap">
      <svg class="cb-ico" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" focusable="false">
        <path fill="#ffffff" d="M257.5 27.6c-.8-5.4-4.9-9.8-10.3-10.6c-22.1-3.1-44.6.9-64.4 11.4l-74 39.5C89.1 78.4 73.2 94.9 63.4 115L26.7 190.6c-9.8 20.1-13 42.9-9.1 64.9l14.5 82.8c3.9 22.1 14.6 42.3 30.7 57.9l60.3 58.4c16.1 15.6 36.6 25.6 58.7 28.7l83 11.7c22.1 3.1 44.6-.9 64.4-11.4l74-39.5c19.7-10.5 35.6-27 45.4-47.2l36.7-75.5c9.8-20.1 13-42.9 9.1-64.9c-.9-5.3-5.3-9.3-10.6-10.1c-51.5-8.2-92.8-47.1-104.5-97.4c-1.8-7.6-8-13.4-15.7-14.6c-54.6-8.7-97.7-52-106.2-106.8zM208 144a32 32 0 1 1 0 64 32 32 0 1 1 0-64zM144 336a32 32 0 1 1 64 0 32 32 0 1 1 0-64zm224-64a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/>
      </svg>
    </span>`;
    btn.addEventListener("click",()=>window.cookieBanner.open());
    document.body.appendChild(btn);
  }

  window.cookieBanner={
    open:()=>build(readState()),
    reset:()=>{ setCookieState({...DEFAULTS,consent:"no"}); build(readState()); },
    get:()=>readState(),
    getRaw:()=>getCookieRaw()
  };

  window.addEventListener("load",()=>{
    addLauncher();
    if(!hasChoice()) build(readState());
  });
})();
</script>

Kod zmiennej dla reklam (advertisement)

function () {
  function getConsentCookie() {
    var name = 'consent='; // Nazwa ciasteczka ze zrzutu ekranu
    var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków specjalnych
    var parts = decodedCookie.split(';');
    
    for (var i = 0; i < parts.length; i++) {
      var c = parts[i];
      // Usuwanie spacji wiodących (alternatywa dla pętli while)
      c = c.trim(); 
      if (c.indexOf(name) === 0) {
        return c.substring(name.length, c.length);
      }
    }
    return '';
  }

  var v = getConsentCookie();

  // Sprawdzamy zgodę
  if (v.indexOf('advertisement:yes') > -1) {
    return 'granted';
  }
  
  // Sprawdzamy odmowę
  if (v.indexOf('advertisement:no') > -1) {
    return 'denied';
  }

  // Domyślna wartość
  return 'denied';
}

Kod zmiennej dla analityki (analytics)

function () {
  function getConsentCookie() {
    var name = 'consent='; // Nazwa ciasteczka ze zrzutu ekranu
    var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków specjalnych
    var parts = decodedCookie.split(';');
    
    for (var i = 0; i < parts.length; i++) {
      var c = parts[i];
      // Usuwanie spacji wiodących (alternatywa dla pętli while)
      c = c.trim(); 
      if (c.indexOf(name) === 0) {
        return c.substring(name.length, c.length);
      }
    }
    return '';
  }

  var v = getConsentCookie();

  // Sprawdzamy zgodę
  if (v.indexOf('analytics:yes') > -1) {
    return 'granted';
  }
  
  // Sprawdzamy odmowę
  if (v.indexOf('analytics:no') > -1) {
    return 'denied';
  }

  // Domyślna wartość
  return 'denied';
}

Kod zmiennej dla funkcjnych (functionality)

function () {
  function getConsentCookie() {
    var nameA = 'consent=';
    var nameB = 'cookieyes-consent=';
    var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków
    var parts = decodedCookie.split(';');
    
    for (var i = 0; i < parts.length; i++) {
      var c = parts[i].trim();
      if (c.indexOf(nameA) === 0) return c.substring(nameA.length, c.length);
      if (c.indexOf(nameB) === 0) return c.substring(nameB.length, c.length);
    }
    return '';
  }

  var v = getConsentCookie();

  // Sprawdzamy zgodę
  if (v.indexOf('functionality:yes') > -1) {
    return 'granted';
  }
  
  // Sprawdzamy odmowę
  if (v.indexOf('functionality:no') > -1) {
    return 'denied';
  }

  // Domyślna wartość
  return 'denied';
}

Kod zmiennej dla personaliacji (personalization)

function () {
  function getConsentCookie() {
    var nameA = 'consent=';
    var nameB = 'cookieyes-consent=';
    var decodedCookie = decodeURIComponent(document.cookie); // Dekodowanie znaków
    var parts = decodedCookie.split(';');
    
    for (var i = 0; i < parts.length; i++) {
      var c = parts[i].trim();
      if (c.indexOf(nameA) === 0) return c.substring(nameA.length, c.length);
      if (c.indexOf(nameB) === 0) return c.substring(nameB.length, c.length);
    }
    return '';
  }

  var v = getConsentCookie();

  // Sprawdzamy zgodę
  if (v.indexOf('personalization:yes') > -1) {
    return 'granted';
  }
  
  // Sprawdzamy odmowę
  if (v.indexOf('personalization:no') > -1) {
    return 'denied';
  }

  // Domyślna wartość
  return 'denied';
}

Dodaj komentarz

Napisz do mnie

Paweł Piekarski
Paweł Piekarski PhD, Analityk eCommerce Marketing oparty na danych

Dane kontaktowe

+48 725 473 745

poczta@pawelpiekarski.pl

linkedin.com/in/pawelpiekarskipl

    Wysyłając wiadomość, zgadzasz się na kontakt i przetwarzanie danych zgodnie z polityką prywatności.