import { Logger } from './logger' import Timeout from './timeout' import EnhancedContentApi from '../enhancedContent' export default class TimeOnPageTracker { #logger: Logger #ecApi?: EnhancedContentApi #timeOnPage = 0 // The total time on page #timeOnPageSessionStart = Date.now() // The current time on page session start time #visibilitychangeHandler: () => void #beforeunloadHandler: () => void #timeout: Timeout public constructor(logger: Logger, ecApi?: EnhancedContentApi) { this.#logger = logger this.#ecApi = ecApi this.#visibilitychangeHandler = this.#visibilitychangeCallback.bind(this) this.#beforeunloadHandler = this.sendEvent.bind(this) this.#timeout = new Timeout() } public start(): void { document.addEventListener('visibilitychange', this.#visibilitychangeHandler) window.addEventListener('beforeunload', this.#beforeunloadHandler) this.#timeout.start(this.sendEvent.bind(this)) } public stop(): void { document.removeEventListener('visibilitychange', this.#visibilitychangeHandler) window.removeEventListener('beforeunload', this.#beforeunloadHandler) this.#timeout.clear() } public restart(): void { this.stop() this.#timeOnPage = 0 this.#timeOnPageSessionStart = Date.now() this.start() } public sendEvent(): void { const currentTime = Date.now() this.#timeOnPage += (currentTime - this.#timeOnPageSessionStart) / 1000 this.#timeOnPageSessionStart = currentTime this.#logger.log('time_on_page', { timeOnPage: this.#timeOnPage, lastEcRenderConfig: this.#ecApi?.lastRenderConfig, }) } #visibilitychangeCallback(): void { // document.visibilityState returns the current visibility state of the document. // It returns these states as “hidden“, “visible”, and “prerender”. // - hidden means the current tab is not in focus, either switched to another tab, minimized, or closed // - visible means the current tab is in focus // - prerender means the page has been loaded but the user has not viewed the page if (document.visibilityState === 'visible') { // If the document is change to visible, reset the session start time and continue the timeout this.#timeOnPageSessionStart = Date.now() } else { // If the document is not visible, it means the user is swtiching to another tab or closing the tab // Calculate the time on page and log it, also remove the timeout this.sendEvent() this.#timeout.clear() } } }