fix: reduce false positives in rage click and dead click detection
- Skip rage clicks when text is selected (triple-click to select) - Exclude tabindex="-1" elements from dead click interactive selector - Observe document.body for DOM changes (modals, drawers, overlays) - Listen for popstate/hashchange to detect SPA navigations
This commit is contained in:
@@ -579,6 +579,15 @@
|
||||
|
||||
// * Check if rage click threshold is met
|
||||
if (entry.times.length >= RAGE_CLICK_THRESHOLD) {
|
||||
// * Skip if user is selecting text (triple-click to select paragraph)
|
||||
try {
|
||||
var sel = window.getSelection();
|
||||
if (sel && sel.toString().trim().length > 0) {
|
||||
entry.times = [];
|
||||
return;
|
||||
}
|
||||
} catch (ex) {}
|
||||
|
||||
// * Debounce: max one rage_click per element per 5 seconds
|
||||
if (now - entry.lastFired >= RAGE_CLICK_DEBOUNCE) {
|
||||
var clickCount = entry.times.length;
|
||||
@@ -602,7 +611,7 @@
|
||||
// * or network request occurs within 1 second
|
||||
// * Opt-out: add data-no-dead to the script tag
|
||||
if (!script.hasAttribute('data-no-dead')) {
|
||||
var INTERACTIVE_SELECTOR = 'a,button,input,select,textarea,[role="button"],[role="link"],[role="tab"],[role="menuitem"],[onclick],[tabindex]';
|
||||
var INTERACTIVE_SELECTOR = 'a,button,input,select,textarea,[role="button"],[role="link"],[role="tab"],[role="menuitem"],[onclick],[tabindex]:not([tabindex="-1"])';
|
||||
var DEAD_CLICK_DEBOUNCE = 10000;
|
||||
var DEAD_CLEANUP_INTERVAL = 30000;
|
||||
var deadClickDebounce = {}; // * selector -> lastFiredTimestamp
|
||||
@@ -665,11 +674,15 @@
|
||||
var mutationObs = null;
|
||||
var perfObs = null;
|
||||
var cleanupTimer = null;
|
||||
var popstateHandler = null;
|
||||
var hashchangeHandler = null;
|
||||
|
||||
function cleanup() {
|
||||
if (mutationObs) { try { mutationObs.disconnect(); } catch (ex) {} mutationObs = null; }
|
||||
if (perfObs) { try { perfObs.disconnect(); } catch (ex) {} perfObs = null; }
|
||||
if (cleanupTimer) { clearTimeout(cleanupTimer); cleanupTimer = null; }
|
||||
if (popstateHandler) { window.removeEventListener('popstate', popstateHandler); popstateHandler = null; }
|
||||
if (hashchangeHandler) { window.removeEventListener('hashchange', hashchangeHandler); hashchangeHandler = null; }
|
||||
}
|
||||
|
||||
function onEffect() {
|
||||
@@ -677,7 +690,7 @@
|
||||
cleanup();
|
||||
}
|
||||
|
||||
// * Set up MutationObserver to detect DOM changes on the element and its parent
|
||||
// * Set up MutationObserver to detect DOM changes on the element, its parent, and body
|
||||
if (typeof MutationObserver !== 'undefined') {
|
||||
try {
|
||||
mutationObs = new MutationObserver(function() {
|
||||
@@ -689,6 +702,8 @@
|
||||
if (parent && parent.tagName !== 'HTML' && parent.tagName !== 'BODY') {
|
||||
mutationObs.observe(parent, { childList: true });
|
||||
}
|
||||
// * Also observe body for top-level DOM changes (modals, drawers, overlays, toasts)
|
||||
mutationObs.observe(document.body, { childList: true, attributes: true });
|
||||
} catch (ex) {
|
||||
mutationObs = null;
|
||||
}
|
||||
@@ -706,6 +721,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// * Listen for SPA navigation events (popstate, hashchange)
|
||||
popstateHandler = function() { onEffect(); };
|
||||
hashchangeHandler = function() { onEffect(); };
|
||||
window.addEventListener('popstate', popstateHandler);
|
||||
window.addEventListener('hashchange', hashchangeHandler);
|
||||
|
||||
// * After 1 second, check if any effect was detected
|
||||
cleanupTimer = setTimeout(function() {
|
||||
cleanup();
|
||||
|
||||
Reference in New Issue
Block a user