UNPKG

page-tracker-logger

Version:

A lightweight client-side JavaScript utility to track user behavior like clicks, tab switches, typing, idle time, and more.

176 lines (153 loc) 4.93 kB
export default async function pageTracker(options = {}) { const { switchTabTrace = true, clickTrace = true, typingTrace = true, externalPasteTrace = true, localStorage: { save = false, key = "activity" } = {}, fetch: { senddata = false, url = "" } = {} } = options; let currRoute = window.location.href; let visitPageTime = Date.now(); let RouteStatus = { url: "", visitPageTime: null, levePage: null, duration: null }; let clickLogs = [], switchTab = [], countswitchTab = 0, leaveTab = null; let typingStatus = [], typing = false, typingStartTime = null, typingTimerId = null; let copyStatusfromExtrenalSource = [], lastCopiedText = ""; let idleLogs = [], totalIdleTime = 0, idleStartTime = null, idleTimer = null; let idleThreshold = 10000; function toReadableTime(seconds) { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins > 0 ? mins + " min " : ""}${secs} sec`; } if (clickTrace) { document.addEventListener("click", (e) => { let ele = e.target; clickLogs.push({ tag: ele?.tagName, innerText: ele?.innerText || "", id: ele?.id || "", className: ele.className || "", timestamp: Date.now(), }); }); } if (switchTabTrace) { document.addEventListener("visibilitychange", () => { if (document.hidden) { countswitchTab++; leaveTab = Date.now(); } if (!document.hidden && leaveTab) { let duration = (Date.now() - leaveTab) / 1000; switchTab.push({ countswitchTab, duration: toReadableTime(duration) }); leaveTab = null; } }); } if (typingTrace) { document.addEventListener("keydown", (e) => { if (e.key.length === 1 || e.key === "Backspace" || e.key === "Enter") { if (!typing) { typing = true; typingStartTime = Date.now(); } clearTimeout(typingTimerId); typingTimerId = setTimeout(() => { typing = false; const duration = (Date.now() - typingStartTime) / 1000; typingStatus.push({ timestampStartAt: typingStartTime, timestampEndAt: Date.now(), duration: toReadableTime(duration), }); typingStartTime = null; }, 3000); } }); } if (externalPasteTrace) { document.addEventListener("copy", () => { lastCopiedText = window.getSelection().toString(); }); document.addEventListener("paste", (e) => { const pastedText = e.clipboardData.getData("text"); const isFromExternal = pastedText !== lastCopiedText; copyStatusfromExtrenalSource.push({ type: "paste", content: pastedText, fromExternal: isFromExternal, timestamp: Date.now(), }); }); } function startIdleTimer() { clearTimeout(idleTimer); idleTimer = setTimeout(() => { idleStartTime = Date.now(); }, idleThreshold); } function onUserActivity() { if (idleStartTime !== null) { const idleDuration = (Date.now() - idleStartTime) / 1000; totalIdleTime += idleDuration; idleLogs.push({ idleStart: idleStartTime, idleEnd: Date.now(), duration: toReadableTime(idleDuration), }); idleStartTime = null; } startIdleTimer(); } ["mousemove", "keydown", "click", "scroll", "touchstart"].forEach((event) => { document.addEventListener(event, onUserActivity); }); startIdleTimer(); window.addEventListener("beforeunload", async () => { if (idleStartTime !== null) { const idleDuration = (Date.now() - idleStartTime) / 1000; totalIdleTime += idleDuration; idleLogs.push({ idleStart: idleStartTime, idleEnd: Date.now(), duration: toReadableTime(idleDuration), }); } RouteStatus.url = currRoute; RouteStatus.visitPageTime = visitPageTime; RouteStatus.levePage = Date.now(); RouteStatus.duration = toReadableTime((RouteStatus.levePage - visitPageTime) / 1000); const finalData = { RouteStatus, clickLogs, switchTab, typingStatus, copyStatusfromExtrenalSource, idleLogs, totalIdleTime: toReadableTime(totalIdleTime), }; if (save && key) { localStorage.setItem(key, JSON.stringify(finalData)); } if (senddata && url) { try { await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(finalData), }); } catch (err) { console.error("Failed to send tracking data:", err); } } }); }