Exit Popup Rendszer

Eszköztípus, munkaidő és kosár állapot alapján intelligensen triggerelt oldalelhagyó popup – OpenCart és Journal3 témához.

📁 exit-popup.js 🛒 OpenCart 🎨 Journal3 📍 Journal3 Popup modul

Áttekintés

Az exit popup rendszer célja, hogy az oldalról távozni készülő látogatókat visszatartsa, és vásárlásra, illetve kapcsolatfelvételre ösztönözze őket. A szkript a Journal3 téma beépített popup modulját hívja meg, saját maga nem tartalmaz popup megjelenítési logikát.

Telefonos popup – ID 359 Kosáras popup – ID 371 Munkaidő: H–P 08:00–16:00 Breakpoint: 1024px
PopupIDMikor jelenik meg
Telefonos popup 359 Munkaidőben – mobilon inaktivitás után, desktopon exit intentnél
Kosáras popup 371 Munkaidőn túl, desktopon, tele kosár esetén exit intentnél
1. ábra – A popup megjelenítés

Hogyan működik

A szkript a DOMContentLoaded eseményre fut le. Megvizsgálja az eszköz szélességét, és az alapján regisztrál eseményfigyelőt – inaktivitás alapút mobilon, exit intent alapút desktopon.

Munkaidő meghatározás

Az isBusinessHours() függvény a látogató böngészőjének helyi idejét vizsgálja. Munkaidőnek a hétfőtől péntekig tartó, 08:00–15:59 közötti időszak számít.

Kosár állapot olvasása

A hasItemsInCart() függvény a #cart-items ID-jú DOM elem szövegtartalmát olvassa. Ha az egész szám értéke nagyobb nullánál, a kosár teli. Ha az elem nem található, a függvény false-t ad vissza.

Ismétlés-védelem

A popupShown boolean jelző biztosítja, hogy egy oldalletöltésen belül csak egyszer jelenjen meg popup, még akkor is, ha a trigger feltétel többször is teljesülne.

Döntési logika

Eszköz típusa

├── Mobil / Tablet (szélesség < 1024px)
│ └── 10 mp inaktivitás után
│ └── Telefonos popup (359) — munkaidőtől függetlenül

└── Desktop (szélesség ≥ 1024px)
└── Egér elhagyja a viewport tetejét (exit intent)
├── Munkaidőben → Telefonos popup (359)
├── Munkaidőn túl + tele kosár → Kosáras popup (371)
└── Munkaidőn túl + üres kosár → Semmi
EszközMunkaidőKosárTriggerPopup
Mobil/Tabletbármikorbármilyen10s inaktivitásTelefonos 359
Desktopigenbármilyenexit intentTelefonos 359
Desktopnemteleexit intentKosáras 371
DesktopnemüresSemmi

Telepítés

  1. Fájl feltöltése FTP-n
    Töltsd fel az exit-popup.js fájlt a következő helyre:
    PATH
    /public_html/[domain]/catalog/view/theme/journal3/js/exit-popup.js
  2. Szkript beillesztése a témába
    A Journal3 admin felületén navigálj a Theme Settings → Custom JS szekcióba, és add hozzá:
    HTML
    <script src="/catalog/view/theme/journal3/js/exit-popup.js"></script>
    ⚠️
    A szkriptnek a Journal3 Popup modul JS kódja után kell betöltődnie, különben az open_popup() függvény nem lesz elérhető.
  3. Popup ID-k ellenőrzése
    Journal3 adminban ellenőrizd, hogy a két popup a szkriptben megadott ID-kon él. Ha eltérnek, módosítsd az exit-popup.js tetején lévő konstansokat (lásd Beállítás).
  4. Tesztelés
    Teszteld DevToolsban Desktop nézetben munkaidőn kívül egy teli kosárral, majd mobil nézetben 10 másodperc inaktivitás után.
🖼
Journal3 admin – Popup lista és ID-k
images/journal3-popup-admin.jpg
2. ábra – A Journal3 admin felületén ellenőrizd az aktív popup ID-kat

Beállítás

Popup ID-k módosítása

Az exit-popup.js első két sorában állíthatók be:

JS
const phonePopupId = 359; // telefonos popup
const cartPopupId  = 371; // kosáras popup

Munkaidő módosítása

JS
function isBusinessHours() {
  const now  = new Date();
  const day  = now.getDay();    // 0 = vasárnap, 6 = szombat
  const hour = now.getHours();
  const isWeekday     = day >= 1 && day <= 5;   // H–P
  const isWorkingHour = hour >= 8 && hour < 16; // 08:00–15:59
  return isWeekday && isWorkingHour;
}

Példa – reggel 7-től este 18-ig, hétvégén is:

JS
const isWeekday     = day >= 0 && day <= 6;
const isWorkingHour = hour >= 7 && hour < 18;

Inaktivitási időkorlát (mobil)

JS
inactivityTimer = setTimeout(function () {
  showPopup(phonePopupId);
}, 10000); // ← ms, pl. 15000 = 15 másodperc

Desktop/mobil törési pont

Az 1024px-es határérték két helyen szerepel a kódban:

JS
if (window.innerWidth < 1024)  { /* mobil */ }
if (window.innerWidth >= 1024) { /* desktop */ }

Popup tartalmak

A popup HTML tartalmát a Journal3 admin felületén kell beállítani az adott ID-jú popuphoz.

Telefonos popup (ID: 359)

Custom URL: javascript:open_popup(359);

HTML
<div style="text-align: center; padding: 20px;">
  <h2 style="margin-bottom: 15px;">Nem találtad meg, amit kerestél?</h2>
  <p style="font-size: 16px; margin-bottom: 20px;">
    Ne menj tovább – hívj fel most, segítünk!
  </p>
  <a href="tel:+36302626381"
     style="display: inline-block; background-color: #007bff; color: #fff;
            text-decoration: none; padding: 12px 24px; font-size: 16px;
            border-radius: 6px;">
    📞 Hívás most: +36 30 262 6381
  </a>
</div>

Kosáras popup (ID: 371)

Custom URL: javascript:open_popup(371);

HTML
<div style="text-align: center; padding: 20px;">
  <h2 style="margin-bottom: 15px;">⏰ Termékek várnak a kosaradban!</h2>
  <p style="font-size: 16px; margin-bottom: 20px;">
    Ne hagyd félbe – fejezd be a rendelést most!
  </p>
  <a href="/index.php?route=checkout/cart"
     style="display: inline-block; background-color: #28a745; color: #fff;
            text-decoration: none; padding: 12px 24px; font-size: 18px;
            border-radius: 6px; font-weight: bold;">
    🛒 Vissza a kosárhoz
  </a>
</div>

Követelmények

KövetelményRészlet
PlatformOpenCart (bármely verzió)
TémaJournal3
Journal3 Popup modulAktív és konfigurált – ez biztosítja az open_popup() globális függvényt
Kosár elem#cart-items ID-jú DOM elem, melynek szövegtartalma a kosárban lévő termékek száma (egész szám)
BöngészőMinden modern böngésző (Chrome, Firefox, Safari, Edge)
SzerverNem szükséges szerverfeltétel – tisztán kliens-oldali szkript
ℹ️
A szkript a látogató helyi idejét használja a munkaidő meghatározásához. Ha a célközönség más időzónában van, ezt figyelembe kell venni.

Ismert hibák és megoldásaik

🖥️ Desktopon nem jelenik meg a popup

Ellenőrizd ebben a sorrendben:

  1. Munkaidőn kívül + üres kosár? → Ez tervezett viselkedés, nem hiba.
  2. open_popup elérhető-e? → DevTools konzolban: typeof open_popup – ha "undefined", a Journal3 Popup modul nincs aktív, vagy a szkript betöltési sorrendje helytelen.
  3. Helyes popup ID? → Ellenőrizd a Journal3 adminban az aktuális ID-kat.
  4. #cart-items elem? → Konzolban: document.querySelector('#cart-items')
📱 Mobilon nem jelenik meg a popup
  • Ellenőrizd, hogy a window.innerWidth valóban kisebb-e 1024px-nél (DevTools → Device Toolbar).
  • Ellenőrizd, hogy az open_popup() függvény elérhető-e mobilon.
  • A 10 másodperc letelte előtt interaktáltál-e az oldallal? Bármely érintés, görgetés vagy kattintás visszaállítja a timert.
🔁 Popup többször jelenik meg ugyanazon az oldalon

A popupShown jelző csak az aktuális oldalletöltésre vonatkozik. SPA-szerű navigáció esetén (pushState) az oldal újratöltés nélkül változhat, és a jelző resetelődhet.

Megoldás: sessionStorage használata:

JS
function showPopup(popupId) {
  if (!sessionStorage.getItem('popupShown') &&
      typeof open_popup === 'function') {
    open_popup(popupId);
    sessionStorage.setItem('popupShown', '1');
  }
}
🛒 Munkaidőn túl üres kosárnál is megjelenik a popup

Valószínűleg a #cart-items DOM elem hiányzik, így a hasItemsInCart() false-t ad vissza – de ha valahol a logika megkerüli ezt, ellenőrizd az elem jelenlétét és szövegtartalmát.

DevTools konzolban:

JS
document.querySelector('#cart-items')?.textContent
⏰ Helytelen munkaidő-meghatározás

A szkript a látogató böngészőjének helyi idejét használja – nem a szerver idejét. Ha a célközönség más időzónában van, ez eltérést okozhat.

Szerver-oldali időzóna-alapú megoldáshoz a munkaidő meghatározását az OpenCart kontroller szinten kell elvégezni, és a kapott értéket JS változóként kell átadni az oldalnak.

Forráskód

Fájl helye a szerveren:

PATH
/public_html/[domain]/catalog/view/theme/journal3/js/exit-popup.js
JS – exit-popup.js
document.addEventListener('DOMContentLoaded', function () {
  const phonePopupId = 359; // telefonos popup
  const cartPopupId  = 371; // kosáras popup
  let popupShown = false;

  function isBusinessHours() {
    const now  = new Date();
    const day  = now.getDay();    // 0 = vasárnap, 6 = szombat
    const hour = now.getHours();
    const isWeekday     = day >= 1 && day <= 5;
    const isWorkingHour = hour >= 8 && hour < 16;
    return isWeekday && isWorkingHour;
  }

  function hasItemsInCart() {
    const cartItems = document.querySelector('#cart-items');
    if (cartItems) {
      const count = parseInt(cartItems.textContent.trim());
      return count > 0;
    }
    return false;
  }

  function showPopup(popupId) {
    if (!popupShown && typeof open_popup === 'function') {
      open_popup(popupId);
      popupShown = true;
    }
  }

  // Mobil/Tablet: inaktivitás után 10s-cel
  if (window.innerWidth < 1024) {
    let inactivityTimer;
    function resetTimer() {
      clearTimeout(inactivityTimer);
      inactivityTimer = setTimeout(function () {
        showPopup(phonePopupId);
      }, 10000);
    }
    ['touchstart', 'touchend', 'scroll', 'click'].forEach(function (e) {
      document.addEventListener(e, resetTimer, { passive: true });
    });
    resetTimer();
  }

  // Desktop: kilépési szándék
  if (window.innerWidth >= 1024) {
    document.documentElement.addEventListener('mouseleave', function (e) {
      if (e.clientY <= 0) {
        if (isBusinessHours()) {
          showPopup(phonePopupId);
        } else if (hasItemsInCart()) {
          showPopup(cartPopupId);
        }
      }
    });
  }
});