Display number of visitors on each product page With Firebase

Squarespace does not expose its analytical metrics for front-end use. I had to use firebase once again to achieve this. The end result is that each product page in my shop displays how many visitors are also on that page. You can see this working if you navigate to any product in my shop and look just above the comment’s section and below the buy button. Chance is big that it will say you are the only one here, nevertheless I tried to keep the overhead to a minimum, potentially accommodating a large number of viewers, or potential customers. This involved creating a server side clean-up function which will not be presented here, however you can do some clean-up on the front end as well, should you want it. Some of the front-end initiated clean-up is implemented already. To get to the point - upon entering any product page I create a count display element which is just a div, then there is a bit of code with a mutation observer to embed it in the right place, because my comments section is also embedded dynamically and I needed to insert the counter before the comments. Proceeding, we are creating a unique ID for each visitor per product page and storing that information in the real time database. We also retrieve the information and count the number of logged visitors on the page we are viewing, subsequently displaying that information.

HTML:

JAVASCRIPT:

<script type="module">
import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.12.5/firebase-app.js';
import { getDatabase, ref, set, remove, serverTimestamp, onValue, get } from 'https://www.gstatic.com/firebasejs/10.12.5/firebase-database.js';
const app = initializeApp(firebaseConfig);
const db = getDatabase(app);
document.addEventListener('DOMContentLoaded', function() {
//are we on product page
const subString = 'shop/p/';
const getURL = window.location.href;
if (getURL.includes(subString)) {
// Create the count element
const visitorCountMainContainer = document.createElement('div');
visitorCountMainContainer.id = 'visitorCountMainContainer';
const visitorCountContainer = document.createElement('div');
visitorCountContainer.id = 'visitorCountContainer';
const visitorCount = document.createElement('div');
visitorCount.id = 'visitorCount';
visitorCountMainContainer.appendChild(visitorCountContainer);
visitorCountContainer.appendChild(visitorCount);
//wait for comments to be added then insert the count
const productItem = document.querySelector('.ProductItem');
function insertVisitorCount() {
const commentsPlace = document.getElementById('commentsMainContainer');
if (productItem && commentsPlace && productItem.contains(commentsPlace)) {
productItem.insertBefore(visitorCountMainContainer, commentsPlace);
return true;
}
return false;
}
const observer = new MutationObserver(() => {
if (insertVisitorCount()) {
observer.disconnect();
}
});
if (productItem) {
observer.observe(productItem, { childList: true, subtree: true });
}
//generate product and visitor IDs for firebase
const productId = getURL.split(subString)[1];
const SESSION_TIMEOUT_MS = 30 * 60 * 1000; //time before we count user as inactive
let lastActivityTime = Date.now();
let sessionTimeout;
let visitorActive = false;
function generateVisitorId() {
let visitorId = localStorage.getItem('visitorId');
if (!visitorId) {
visitorId = 'visitor-' + Math.random().toString(36).substr(2, 9);
localStorage.setItem('visitorId', visitorId);
}
return visitorId;
}
const visitorId = generateVisitorId();
const visitorRef = ref(db, `activeVisitors/${productId}/${visitorId}`);
async function setVisitorActive() {
if (visitorActive === false) {
visitorActive = true;
try {
const expiresAt = Date.now() + SESSION_TIMEOUT_MS;
await set(visitorRef, {
visitorId: visitorId,
lastActive: serverTimestamp(),
expiresAt: expiresAt
});
resetSessionTimeout();
} catch (error) {
console.error("Error setting visitor active: ", error);
}
}
}
async function removeVisitor() {
visitorActive = false;
try {
await remove(visitorRef);
} catch (error) {
console.error("Error removing visitor: ", error);
}
}
function resetSessionTimeout() {
visitorActive = true;
clearTimeout(sessionTimeout);
lastActivityTime = Date.now();
sessionTimeout = setTimeout(() => handleSessionTimeout(), SESSION_TIMEOUT_MS);
}
async function handleSessionTimeout() {
console.log("Visitor inactive, removing session");
await removeVisitor();
}
function displayActiveVisitorCount() {
const activeVisitorsRef = ref(db, `activeVisitors/${productId}`);
get(activeVisitorsRef).then((snapshot) => {
const visitorsData = snapshot.val();
const activeVisitors = visitorsData ? Object.keys(visitorsData).length : 0;
const activeOtherVisitors = activeVisitors-1;
const visitorText = activeOtherVisitors === 0 
? 'You are the only one viewing this product, in the whole world.' 
: activeVisitors === 1 
? '1 other potential customer is also viewing this product.' 
: `${activeVisitors} potential customers are viewing this product.`;
visitorCount.innerText = visitorText;
});
}
window.addEventListener('beforeunload', async () => {
await removeVisitor();
});
setVisitorActive();
displayActiveVisitorCount();
}
});
</script>
CSS:

#visitorCountMainContainer {
  display: flex;
  justify-content: flex-end;
  width: 100%;
  margin-top: -10px;
}
Previous
Previous

xml product sheet generator for google merchant center

Next
Next

Display selected product variant’s associated image in shop