There is function called getTimeRemaining, which shows always 15 minutes in countdown in pop-up banner. Banner is injected to the website via JS SDK. Problem is, banner shows always multiplied numbers in countdown after refresh. I call the function in another function, but i dont know, why is javascript remembering first number even after refresh, when there is no session/localStorage. Can you help me? Whan can i do to prevent this?
return (function insertBanner(data) {
insertBanner.inPreview = false;
insertBanner.sdk = window.exponea;
insertBanner.data = data;
insertBanner.html = "<div class="weblayer--box-countdown">n <div class="inner">n <button aria-label="Close" class="close">n <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">n <path d="M2.99867 13L12.998 3" stroke="rgba(255,255,255,0.5)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>n <path d="M12.9987 13L2.998 3" stroke="rgba(255,255,255,0.5)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>n </svg>n </button>n <h2 class="title">WINTER SALE</h2>n <div class="countdown">n <div class="time" style="display:none">n <div class="number hours">00</div>n <div class="unit">Hours</div>n </div>n <div class="time">n <div class="number minutes">00</div>n <div class="unit">Minutes</div>n </div>n <div class="time">n <div class="number seconds">00</div>n <div class="unit">Seconds</div>n </div>n </div>n <h3 class="subtitle">Complete your order in 15 minutes and GET 15% OFF all eyewear.</h3>n <div class="discount">n t<p>use code: n tt<span>GET15</span>n t</p>n t<button class="main-cta">COPY</button>n </div>n <p class="close-text">No, thank you, Iu2019d rather pay full price.</p>n </div>n</div>";
insertBanner.style = "@import url("https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap");n/*n * Jinja Params Start. Do not edit these values.n */nnnnnnnnnnnnnnnnnn/*n * Jinja Params Endn */n.weblayer--box-countdown {n display: flex;n box-sizing: border-box;n font-family: "Lato", Arial, Helvetica, sans-serif;n font-size: 1rem;n /* to prevent inheriting fs of element it's appended to */n line-height: normal;n text-decoration: inherit;n cursor: default;n position: fixed;n z-index: 2147483000;n /* close to the highest possible z-index */n width: 540px;n box-shadow: 0 0.25em 0.5em rgba(0, 0, 0, 0.4);n border-radius: 5px;n margin: 10px;n overflow: hidden;n background-color: #194E48;n padding: 20px 0px;n}n.weblayer--box-countdown * {n box-sizing: border-box;n}n.weblayer--box-countdown.horizontal-left {n left: 0;n}n.weblayer--box-countdown.horizontal-right {n right: 0;n}n.weblayer--box-countdown.horizontal-center {n left: 50%;n transform: translateX(-50%);n}n.weblayer--box-countdown.vertical-top {n top: 0;n}n.weblayer--box-countdown.vertical-bottom {n bottom: 0;n}n.weblayer--box-countdown.vertical-center {n top: 50%;n transform: translateY(-50%);n}n.weblayer--box-countdown.vertical-center.horizontal-center {n transform: translate(-50%, -50%);n}n.weblayer--box-countdown > .inner {n flex-wrap: wrap;n display: flex;n width: 100%;n flex-direction: column;n align-items: center;n justify-content: center;n padding: 1.5em;n text-align: center;n position: relative;n}n.weblayer--box-countdown > .inner > .close {n /* inherits fs of 1rem defined in base styles */n font-size: inherit;n line-height: 1px;n /* resets browser styles for button */n color: transparent;n background-color: transparent;n top: -20px;n right: 0;n padding: 0.5em;n /* resets browser styles for button */n border: 0;n position: absolute;n /* base class + 1 to prevent colliding of layers of clickable elements */n z-index: 2147483001;n}n.weblayer--box-countdown > .inner > .close:hover {n cursor: pointer;n}n.weblayer--box-countdown > .inner > .close:focus {n outline: none;n}n.weblayer--box-countdown > .inner .title {ntfont-family: 'Lato';ntfont-style: normal;ntfont-weight: bold;ntfont-size: 38px;ntline-height: 23px;nttext-align: center;ntcolor: #55DAB1;n}n.weblayer--box-countdown > .inner .subtitle {ntfont-family: 'Lato';ntfont-style: normal;ntfont-weight: bold;ntfont-size: 16px;nttext-align: center;ntcolor: #FFFFFF;ntpadding: 0 20%;n}n.weblayer--box-countdown > .inner .countdown {n display: flex;n justify-content: space-around;n width: 100%;n margin: 1.5em 0;n padding: 0 25%;n}n@supports (display: grid) {n .weblayer--box-countdown > .inner .countdown {n display: grid;n grid-template-columns: repeat(auto-fit, minmax(calc( 270px/3 - 3em), 1fr));n grid-gap: 0.5em;n }n}n.weblayer--box-countdown > .inner .countdown .time {n n font-family: Lato;n n background: #55DAB1;n border-radius: 0;n padding: 0.5em;n color: #194E48;n text-align: center;n margin: 0 5pxn}n.weblayer--box-countdown > .inner .countdown .time .number {n font-family: inherit;n font-size: 50px;n font-weight: 700;n line-height: 1.3;n}n.weblayer--box-countdown > .inner .countdown .time .unit {n font-family: inherit;n font-size: 14px;n font-weight: 400;n line-height: 1.3;n display: nonen}n.weblayer--box-countdown > .inner .btn {n width: 100%;n font-family: inherit;n background: transparent;n border: none;n text-decoration: none;n margin: 0;n user-select: none;n outline: none;n color: #FFFFFF;n background: #F84CAC;n border-radius: 8px;n cursor: pointer;n n font-family: Lato;n n font-size: 15px;n font-weight: 900;n line-height: 1.3;n padding: 0.75em 1.5em;n}n.weblayer--box-countdown > .inner .btn:hover {n filter: brightness(110%);n}n.weblayer--box-countdown > .inner .btn:focus {n outline: none;n}n.weblayer--box-countdown .discount {n width: 80%;n background-color: #0D3C36;n display: flex;n flex-flow: row;n font-family: 'Lato',sans-serif;n justify-content: space-evenly;n align-items: center;n padding: 10px;n}nn.weblayer--box-countdown .discount p {n font-style: normal;n font-weight: normal;n font-size: 16px;n text-align: center;n color: #fff;n}n.weblayer--box-countdown .discount span{n color: #55DAB1;n}n.weblayer--box-countdown .discount .main-cta{n width: 140px;n height: 35px;n background-color: #fff;n border: none;n cursor: pointer;n}nn.weblayer--box-countdown .close-text {ntcolor: #fff;nttext-decoration:underline;ntcursor: pointer;ntmargin-top: 20px;n}nn@media screen and (max-width: 600px) {nt.weblayer--box-countdown {nttwidth: 90%;nttmargin: 0;nt}nt.weblayer--box-countdown {nttflex-wrap: nowrap;tnt}nt.weblayer--box-countdown > .inner .countdown {nttdisplay: flex;nt}n}n";
insertBanner.script = function() { /**
* Universal parameters
*/
var PARAM_showAfter = parseInt("0", 10);
var PARAM_removeAfter = parseInt("0", 10);
var PARAM_trigger = "On entry";
var PARAM_parentElement = "body";
var PARAM_positionVertical = "Center";
var PARAM_positionHorizontal = "Center";
var PARAM_enterAnimation = "None";
/**
* Template specific parameters
*/
//var d = new Date();
//var PARAM_countdownDate = d.setHours(24,0,0,0) / 1000;
/*
if (PARAM_countdownDate === -1) {
PARAM_countdownDate = +new Date() / 1000 + 3500;
}
*/
/**
* Initialization
*/
var self = this;
self.clockInterval = NaN;
// Helper Id used to identify the banner on the website, not actual ID of the banner
var bannerSemiId = Math.random().toString(36).substring(5);
// Used in onExit banners to mark if the banner was triggered already
window['__exp_triggered-' + bannerSemiId] = false;
// Resetting some of the parameters while previewing the banner in the app to easily see its appearance
if (self.inPreview) {
// reset the show delay while editing the banner in editor
PARAM_showAfter = 0;
// always append the banner to the body itself
PARAM_parentElement = 'body';
// always show the banner right away
PARAM_trigger = 'On entry';
}
/**
* Basic functions
*/
/**
* Function used to register listener for the trigger that will display the banner
*/
function registerStartTrigger() {
if (PARAM_trigger === 'On exit') {
document.body.addEventListener('mouseout', onExitMouseOutHandler);
} else if (PARAM_trigger === 'On scroll') {
window.addEventListener('scroll', scheduleShowBanner);
} else {
// If 'On entry' or anything unknown start the banner right away
scheduleShowBanner();
}
}
/**
* This function starts the showAfter timer and then displays the banner
*/
function scheduleShowBanner() {
window.removeEventListener('scroll', scheduleShowBanner);
setTimeout(function() {
// Track show event after timer expired
trackEvent('show', false);
// Create and display the banner
requestAnimationFrame(createBanner);
// If removeAfter is provided start the removal timer
if (PARAM_removeAfter > 0) {
setTimeout(function() {
removeBanner();
}, PARAM_removeAfter);
}
}, PARAM_showAfter);
}
/**
* Function used to insert the banner contents into the HTML and adding basic functionality
*/
function createBanner() {
var placeholder = document.createElement('div');
placeholder.insertAdjacentHTML('afterbegin', self.html);
// get the banner reference
var banner = placeholder.firstElementChild;
// add close functionality to the close button
banner.querySelector('.close').onclick = handleCloseButtonClick;
// add classes specifying banner position and animation
banner.className += ' ' + getPositionAndAnimationClasses();
// insert banner CSS into the website
banner.insertAdjacentHTML('afterbegin', '<style>' + self.style + '</style>');
// track clicking on <a> in the banner
var links = banner.querySelectorAll('a');
for (var i = 0; i < links.length; i++) {
trackLink(links[i], 'click', true);
}
banner.querySelector(".main-cta").addEventListener("click", copy_function);
banner.querySelector(".close-text").addEventListener("click", removeBanner);
//var minutesToAdd=15;
//var currentDate = new Date();
//var dt1 = (new Date()).getTime();
//var PARAM_countdownDate = dt1+900000;
var PARAM_countdownDate = new Date((new Date().getTime()) + 15*60000);
//elf.clockInterval = NaN;
//self.clockInterval = 0;
self.clockInterval = initializeClock(banner, PARAM_countdownDate);
attachBannerToDom(banner);
console.log(self);
}
function copy_function() {
var copyText = document.querySelector(".weblayer--box-countdown .discount span");
var textArea = document.createElement("textarea");
textArea.value = copyText.textContent;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("Copy");
textArea.remove();
}
/**
* Function used to insert the banner HTML to the DOM
* @param banner - html content of the banner
*/
function attachBannerToDom(banner) {
var parentElement = document.querySelector(PARAM_parentElement);
parentElement.insertAdjacentElement('afterbegin', banner);
self.banner = banner;
}
/**
* Creates banner in shadow dom
* @param parentElement
*/
function createBannerInShadowDom(banner, parentElement) {
var shadowHost = document.createElement('div');
shadowHost.innerHTML = getFontsImport();
var shadowRoot = shadowHost.attachShadow({ mode: 'open' });
shadowRoot.appendChild(banner);
parentElement.insertAdjacentElement('afterbegin', shadowHost);
self.banner = shadowHost;
}
/**
* Function used to add fonts import to element
* The font import does not work inside shadow DOM, it has to be declared outside
* @returns string - style tag with fonts import
*/
function getFontsImport() {
var results = self.style.match(/@import url(["'].+?['"])/g);
return results && results.length ? '<style>' + results.join(';') + ';</style>' : '';
}
/**
* Function used to remove the banner from the website
*/
function removeBanner() {
if (self.banner && self.banner.parentNode) {
self.banner.parentNode.removeChild(self.banner);
}
if (!isNaN(self.clockInterval)) {
clearInterval(self.clockInterval);
self.clockInterval = NaN;
}
}
/**
* Function triggered when the closing button is clicked
* @param event - browser click Event
* @returns {boolean}
*/
function handleCloseButtonClick(event) {
removeBanner();
trackEvent('close', true);
// Stop the click event propagation onto parent HTML elements
event.preventDefault();
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
return false;
}
/**
* Function used to track single action
* @param action - string
* @param interactive - boolean
*/
function trackEvent(action, interactive) {
self.sdk.track('banner', getEventProperties(action, interactive));
}
/**
* Function used to add action tracking to element
* @param link - element
* @param action - string
* @param interactive - boolean
*/
function trackLink(link, action, interactive) {
var eventData = getEventProperties(action, interactive);
eventData.link = link.href;
self.sdk.trackLink(link, 'banner', eventData);
}
/**
* Default attributes tracked with every banner event
* @param action - string
* @param interactive - boolean
* @returns object - object to be tracked
*/
function getEventProperties(action, interactive) {
return {
action: action,
banner_id: self.data.banner_id,
banner_name: self.data.banner_name,
banner_type: self.data.banner_type,
variant_id: self.data.variant_id,
variant_name: self.data.variant_name,
interaction: interactive !== false,
};
}
/**
* Function used to start banners with onExit trigger
* @param event - browser mouse event
*/
function onExitMouseOutHandler(event) {
event = event ? event : window.event;
var vpWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
if (event.clientX >= (vpWidth)) {
return;
}
if (event.clientY >= 50) {
return;
}
var from = event.relatedTarget || event.toElement;
if (!from && !window['__exp_triggered-' + bannerSemiId]) {
window['__exp_triggered-' + bannerSemiId] = true;
scheduleShowBanner();
}
}
/**
* Function that returns correct class
*/
function getPositionAndAnimationClasses() {
var verticalClass = {
Top: 'vertical-top',
Center: 'vertical-center',
Bottom: 'vertical-bottom',
}[PARAM_positionVertical] || '';
var horizontalClass = {
Left: 'horizontal-left',
Center: 'horizontal-center',
Right: 'horizontal-right',
}[PARAM_positionHorizontal] || '';
var enterAnimationClass = {
'Fade in': 'enter-fade',
'Slide in': {
Top: {
Left: 'enter-slide-left',
Center: 'enter-slide-up',
Right: 'enter-slide-right',
}[PARAM_positionHorizontal],
Center: {
Left: 'enter-slide-left',
Center: 'enter-slide-up',
Right: 'enter-slide-right',
}[PARAM_positionHorizontal],
Bottom: {
Left: 'enter-slide-left',
Center: 'enter-slide-down',
Right: 'enter-slide-right',
}[PARAM_positionHorizontal],
}[PARAM_positionVertical],
}[PARAM_enterAnimation] || '';
return verticalClass + ' ' + horizontalClass + ' ' + enterAnimationClass;
}
/**
* Template specific functions
*/
/**
* Returns the remaining time of the countdown
*/
function getTimeRemaining(endtime) {
var t = endtime - (new Date() / 1000);
var seconds = Math.floor(t % 60);
var minutes = Math.floor((t / 60) % 60);
var hours = Math.floor((t / (60 * 60)) % 24);
var days = Math.floor(t / (60 * 60 * 24));
return {
total: t,
days: Math.max(days, 0),
hours: Math.max(hours, 0),
minutes: Math.max(minutes, 0),
seconds: Math.max(seconds, 0),
};
}
/**
* Initializes the interval that updates the remaining time in the banner
*/
function initializeClock(banner, endtime) {
//var hoursSpan = banner.querySelector('.hours');
var minutesSpan = banner.querySelector('.minutes');
var secondsSpan = banner.querySelector('.seconds');
function updateClock() {
var t = getTimeRemaining(endtime);
//hoursSpan.innerHTML = ('0' + (t.hours + 24 * t.days)).slice(-2);
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
if (t.total <= 0 && self.inPreview !== true) {
removeBanner();
}
}
updateClock();
return setInterval(updateClock, 1000);
}
/**
* Register the start trigger and return required removal function
*/
registerStartTrigger();
return {
remove: removeBanner,
}; };
insertBanner.remove = (insertBanner.script.call(insertBanner) || {} ).remove;
insertBanner.contextual_personalization = {};
return insertBanner;
})({"banner_group": "", "banner_id": "61f861078e99733a5b05d0cd", "banner_name": "AB - on-exit banner SK", "banner_type": "countdown", "variant_id": 0, "variant_name": "Variant A"});