1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| class BehaviorMonitor { constructor() { this.events = [] this.config = { maxEvents: 100, behaviorTypes: { CLICK: 'click', INPUT: 'input', ROUTE: 'route', API: 'api' } } this.initTrackers() } initTrackers() { document.addEventListener('click', (event) => { const target = event.target this.trackEvent({ type: this.config.behaviorTypes.CLICK, element: target.tagName, content: target.textContent, path: this.getElementPath(target) }) }, true) document.addEventListener('input', this.debounce((event) => { const target = event.target if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') { this.trackEvent({ type: this.config.behaviorTypes.INPUT, element: target.tagName, name: target.name || target.id }) } }, 500), true) this.trackRouteChange() } getElementPath(element) { const path = [] while (element && element.nodeType === Node.ELEMENT_NODE) { let selector = element.tagName.toLowerCase() if (element.id) { selector += `#${element.id}` } else if (element.className) { selector += `.${element.className.split(' ').join('.')}` } path.unshift(selector) element = element.parentNode } return path.join(' > ') } trackRouteChange() { let lastUrl = window.location.href const originalPushState = window.history.pushState window.history.pushState = (...args) => { originalPushState.apply(window.history, args) this.handleUrlChange() } window.addEventListener('popstate', () => { this.handleUrlChange() }) } handleUrlChange() { const currentUrl = window.location.href this.trackEvent({ type: this.config.behaviorTypes.ROUTE, from: lastUrl, to: currentUrl }) lastUrl = currentUrl } debounce(fn, delay) { let timer = null return function(...args) { if (timer) clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, args) }, delay) } } trackEvent(event) { const eventInfo = { ...event, timestamp: Date.now(), url: window.location.href, userAgent: navigator.userAgent } this.events.push(eventInfo) if (this.events.length > this.config.maxEvents) { this.events.shift() } this.reportEvent(eventInfo) } async reportEvent(event) { try { await fetch('/api/events', { method: 'POST', body: JSON.stringify(event) }) } catch (e) { console.error('Failed to report event:', e) } } }
|