I've created a javascript file that gathers some information from the browser - IP address, url, screen dimensions, etc... - and sends that information off to my server to use that information to create a dynamic offering for them.
I have this code installed on dozens of client sites all across the country via a Google Tag Manager.
However, I have one client who has 3 separate locations - and thus 3 separate websites - where the script causes the site to continuously reload.
For testing, we have:
- Asked them to remove all other GTMs except for ours and the problem persists.
- Keep all other GTMs but ours - and the problem no longer persists
- Try in another browser other than Chrome and the problem no longer persists
- Try in incognito within chrome and the problem persists
- Try with a VPN on and the problem no longer persists
- Try to visit another client of mine and they do not experience the problem on other sites
Basically - the problem only exists in chrome (both incognito and non-incognito) but only on their specific sites and only when they're NOT on their VPN.
In order to put a bandaid on the issue, I've put their IP address on a blacklist so the script doesn't load from their computer - but now they're getting complaints from customers that it's happening to them too.
Is there any javascript I can add to my container that loads the "main" script that will listen for a refresh and, once found, will post and AJAX request to my server with information pertaining to why the site was refreshed?
I'm sure my code is conflicting with another script on the site, but I can't debug the issue since it's not happening locally.
Here is the container that loads the main script if the IP is not blacklisted:
// Ips are made up for stack overflowlet wd_blacklistedIPs = ['147.298.45.43','132.84.44.642','67.221.60.17','122.58.249.330'];function wd_isBlacklisted(ip) { return wd_blacklistedIPs.includes(ip);}function wd_loadScript() { let script = document.createElement('script'); script.src = 'https://server.mydomain.com/codeThatGeneratesOverlay.js'; console.log("Loading overlay script"); // Add the script to the body document.body.appendChild(script); console.log("Overlay script loaded");}// Get user's IP addresslet wd_userIPAddress;fetch('https://server.mydomain.com/getIP.php') .then(response => response.json()) .then(data => { wd_userIPAddress = data.ip; if (!wd_isBlacklisted(wd_userIPAddress)) { wd_loadScript(); } else { console.log('Not Loading Overlay'); } }) .catch(error => console.error('Error fetching IP address: ', error));
Here is the "codeThatGeneratesOverlay.js" code (edited for privacy)
// Add Crypto JS// Removed from this post b/c it's publicly available (https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js) and made the post too long to submit // Start our code(function() { // Check the include to see if there's a get parameter in it for loadNow and get it's value let wd_loadNow = false; if (wd_getParameter('loadNow') === 'true' || wd_getParameter('loadNow') === '1') wd_loadNow = true; console.log("loadNow is " + (wd_loadNow ? "true" : "false")); // Clear the session b/c the page is loading: wd_deleteSessionVariable('wd_sessionIdExpiration'); wd_deleteSessionVariable('wd_sessionId'); wd_destroySession(); var wd_sessionId = wd_guid(), wd_operatingSystem = (typeof navigator.appVersion == 'string' ? navigator.appVersion : ''), wd_deviceType = wd_detectDeviceType(); function wd_pageLoaded() { console.log("Page loaded. Getting wd info."); // Get the "thank you" pages wd_getThankYouPages(); let wd_ipAddress = wd_getIpAddress(); if (wd_ipAddress === null || wd_ipAddress === undefined || wd_ipAddress === '' || wd_ipAddress.indexOf("0.0.0.0") >= 0) return; let wd_pageLoad_timestamp = new Date().getTime(), wd_pageUrl = window.location.href, wd_pageTitle = document.title, wd_domain = window.location.hostname, wd_width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, wd_height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, wd_ad = wd_getAd(wd_pageUrl, wd_ipAddress, wd_width, wd_height); // if wd_ad is not null and is a string, json decode it if (wd_ad !== null && typeof wd_ad === 'string') { wd_ad = JSON.parse(wd_ad); } // set the sesion guid expiration variable to 30 seconds from now. The heartbeat function will keep it alive wd_setSessionVariable('wd_sessionIdExpiration', new Date().getTime() + 30000); // run the heartbeat function every 10 seconds indefinitely setInterval(wd_heartbeat, 10000); // Set a listener for page tel links wd_setTelLinks(wd_ipAddress); // Store the page load let url = 'https://server.myDomain.com/stagePageAction.php', data = {'adId': wd_ad.data.adId,'sessionId': wd_sessionId,'ipAddress': wd_ipAddress,'pageUrl': wd_pageUrl,'pageTitle': wd_pageTitle,'domain': wd_domain,'eventTimestamp': wd_pageLoad_timestamp,'event': 'pageLoad','operatingSystem': wd_operatingSystem,'deviceType': wd_deviceType }, response = new XMLHttpRequest(); response.open("POST", url, false); response.setRequestHeader('Content-Type', 'application/json'); response.send(JSON.stringify(data)); let responseString = response.responseText; // If response is not an array, json_decode it. If json_decode fails, set it to an empty array if (typeof responseString !== 'object') { try { responseString = JSON.parse(responseString); } catch (e) { responseString = []; } } // if response['success'] is set and is true, set the session variable "wd_eventId" to the value of response['eventId'] if (responseString['success'] === true) { wd_setSessionVariable('wd_eventId', responseString['data']['eventId']); // create a hidden input called "wd_eventId" and set its value to response['eventId'] let wd_eventId_input = document.createElement('input'); wd_eventId_input.type = 'hidden'; wd_eventId_input.id = 'wd_eventId'; wd_eventId_input.value = responseString['data']['eventId']; document.body.appendChild(wd_eventId_input); } else { wd_deleteSessionVariable('wd_eventId'); } // If the ad is empty, undefined, or null don't do anything if (wd_ad === undefined || wd_ad === null || wd_ad === '') { return; } if (wd_ad.data.adId === undefined || wd_ad.data.adId === null || wd_ad.data.adId === '') { return; } // store the adId wd_setSessionVariable('wd_adId', wd_ad.data.adId); // inject the value of wd_ad into the page in a script tag let wd_script = document.createElement('script'); wd_script.type = 'text/javascript'; wd_script.innerHTML = wd_ad['data']['javascript']; document.head.appendChild(wd_script); } function wd_getAd(pageUrl, ipAddress, width, height) { let domain = "https://server.myDomain.com"), url = domain +'/getAdByDomain.php?pageUrl='+ encodeURIComponent(pageUrl) +'&ipAddress='+ encodeURIComponent(ipAddress) +'&width='+ encodeURIComponent(width) +'&height='+ encodeURIComponent(height), response = new XMLHttpRequest(); response.open("GET", url, false); response.send(); return response.responseText; } function wd_getIpAddress() { var url = "https://server.myDomain.com/getIP.php", xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.send(); return xhr.responseText; } function wd_guid() { // if wd_sessionIdExpiration is not set or is < now's timestamp, destroy the session if (wd_getSessionVariable('wd_sessionIdExpiration') === null || wd_getSessionVariable('wd_sessionIdExpiration') < new Date().getTime()) { wd_deleteSessionVariable('wd_sessionId'); wd_destroySession(); } // if wd_sessionId is set, return it if (wd_getSessionVariable('wd_sessionId') !== null) return wd_getSessionVariable('wd_sessionId'); // if wd_sessionId is not set, set it to a new guid and return it let guid = "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) ); wd_setSessionVariable('wd_sessionId', guid); return guid; } function wd_setSessionVariable(variableName, variableValue) { localStorage.setItem(variableName, variableValue); } function wd_getSessionVariable(variableName) { return localStorage.getItem(variableName) ?? null; } function wd_deleteSessionVariable(variableName) { localStorage.removeItem(variableName); } function wd_destroySession() { localStorage.clear(); } function wd_heartbeat() { // Update wd_sessionIdExpiration to 30 seconds from now wd_setSessionVariable('wd_sessionIdExpiration', new Date().getTime() + 30000); let wd_eventId = wd_getSessionVariable('wd_eventId'), wd_pageExit_timestamp = new Date().getTime(), wd_sessionId = wd_guid('wd_sessionId'), /* Okay to call guid b/c guid checks session */ wd_pageUrl = window.location.href, wd_pageTitle = document.title, wd_domain = window.location.hostname, url = 'https://server.myDomain.com/stashPageAction.php', data = {'eventId': wd_eventId,'adId': wd_getSessionVariable('wd_adId'),'sessionId': wd_sessionId,'pageUrl': wd_pageUrl,'pageTitle': wd_pageTitle,'domain': wd_domain,'eventTimestamp': wd_pageExit_timestamp,'event': 'pageExit' }, xhr = new XMLHttpRequest(); xhr.open("POST", url, false); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); } // Detect device type function wd_detectDeviceType() { const userAgent = navigator.userAgent; const screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; if (/Mobi/.test(userAgent) || /Android/i.test(userAgent)) { return "Mobile"; } else if (screenWidth <= 768) { return "Tablet"; } return "Desktop"; } function wd_setTelLinks(ipAddress, sessionId) { // listen for "a" tags whose "href" starts with "tel:" and add an event listener to them let telLinks = document.querySelectorAll("a[href^=\'tel:\']"); for (let i = 0; i < telLinks.length; i++) { telLinks[i].addEventListener("click", function() { let sessionId = wd_sessionId = wd_guid('wd_sessionId'), data = { ipAddress: ipAddress, sessionId: wd_guid('wd_sessionId'), eventTimestamp: new Date().getTime(), pageUrl: window.location.href, domain: window.location.hostname, link: telLinks[i].href, event: 'telLinkClicked' }, url = 'https://server.mydomain.com/stashUserAction.php', type = 'POST', xhr = new XMLHttpRequest(); xhr.open(type, url, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); }); } } function wd_getThankYouPages() { let currentUrl = window.location.href, domain = window.location.hostname, xhr = new XMLHttpRequest(), token = null; wd_generateHash().then(finalToken => { token = finalToken; xhr.open('POST', 'https://server.myDomain.com/getThankYouPages.php', true); xhr.setRequestHeader('Content-Type', 'application/json'); // Set up onload event handler xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { let response = xhr.responseText; if (response !== null && response !== undefined) wd_send_thank_you_page_event(JSON.parse(response)); } }; // Send post xhr request xhr.send(JSON.stringify({token: token, domain: domain, currentUrl: currentUrl})); }); } function wd_send_thank_you_page_event(wd_thank_you_pages) { let wd_pageUrl = window.location.href; // if the page is a "thank you" page, store the page load and return if (wd_thank_you_pages !== null && wd_thank_you_pages !== undefined) { for (let i = 0; i < wd_thank_you_pages.length; i++) { // If the pageUrl contains the thank you page as either a string, or a regex, store the page load and return let isMatch = false; if (typeof wd_thank_you_pages[i] === 'string') { if (wd_pageUrl.indexOf(wd_thank_you_pages[i]) > -1) { isMatch = true; } else { try { let regex = new RegExp(wd_thank_you_pages[i]); if (regex.test(wd_pageUrl)) { isMatch = true; } } catch(e) {} } } if (!isMatch && typeof wd_thank_you_pages[i] === 'object'&& wd_thank_you_pages[i].test(wd_pageUrl)) { isMatch = true; } if (isMatch) { let data = {'adId': wd_ad.data.adId,'sessionId': wd_sessionId,'ipAddress': wd_ipAddress,'pageUrl': wd_pageUrl,'pageTitle': wd_pageTitle,'domain': wd_domain,'eventTimestamp': wd_pageLoad_timestamp }, url = 'https://server.myDomain.com/saveThankYouPage.php', xhr = new XMLHttpRequest(); xhr.open("POST", url, false); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); break; } } } } async function wd_generateHash() { let domain = window.location.hostname, hashedDomain = await wd_sha256(domain); return domain +'|'+ hashedDomain; } function wd_sha256(message) { return CryptoJS.SHA256(message).toString(CryptoJS.enc.Hex); } // When the page is done loading, run the pageLoaded function console.log("Loading wd scripts."); if (wd_loadNow) { console.log("Loading now."); wd_pageLoaded(); } else { window.addEventListener('load', function () { // Create a div that would typically be hidden by ad blockers let testAd = document.createElement('div'); testAd.innerHTML = ' '; testAd.className = 'adsbox'; document.body.appendChild(testAd); setTimeout(() => { wd_pageLoaded(); /*if (testAd.offsetHeight === 0) { console.log('Ad blocker detected'); } else { console.log('Ad blocker not detected'); }*/ document.body.removeChild(testAd); }, 100); }); } // Generic function used to see if a param exists in a URL string. // Provided here in case you don't know how to do it. // This is not needed for the solution. function wd_getParameter (name, url) { if (!url) url = wd_getScriptName(); name = name.replace(/[\[\]]/g, '\\$&'); let regex = new RegExp('[?&]'+ name +'(=([^&#]*)|&|#|$)'), results = regex.exec(url); if (!results) return null if (!results[2]) return '' return decodeURIComponent(results[2].replace(/\+/g, '')); } // Gets the name of this script (whatever this file runs in) // You can use this name to get parameters just like you would for the window URL :) function wd_getScriptName () { let error = new Error(), source, lastStackFrameRegex = new RegExp(/.+\/(.*?):\d+(:\d+)*$/), currentStackFrameRegex = new RegExp(/getScriptName \(.+\/(.*):\d+:\d+\)/); if ((source = lastStackFrameRegex.exec(error.stack.trim())) && source[1] !== '') return source[1]; else if ((source = currentStackFrameRegex.exec(error.stack.trim()))) return source[1]; else if (error.fileName !== undefined) return error.fileName; }})();
It's noteworthy that even if the overlays are disabled so "wd_getAd()" doesn't even fire, let alone return anything - the page reloading persists... I say that so nobody asks to see the return from that function.
Like I said, the blacklisting is working, and it's the "codeThatGeneratesOverlay.js" that seems to be causing the problem - but it's working on dozens of other sites without issue... The only thing I can think of now to do to troubleshoot is to attempt to see WHY the site is refreshing via an ajax POST to my server.