vbk2025
Forum Replies Created
-
Hi Mostafa
I does fix my issues 🙂
Hope it’s compatible with the rest of the WP Statistics functionality 🙂I haven’t tested it, but maybe something like this?
history.replaceState = function (…args) {
const result = t.originalReplaceState.apply(this, args);
t.handleUrlChange();
// Dispatch a native-like event so other scripts can hook into this
const event = new Event('replaceState');
event.arguments = args;
window.dispatchEvent(event);
return result;
};
history.pushState = function (…args) {
const result = t.originalPushState.apply(this, args);
t.handleUrlChange();
const event = new Event('pushState');
event.arguments = args;
window.dispatchEvent(event);
return result;
}Hi Mostafa,
Thanks again for getting back.
I’ve updated the test code and confirmed that it works correctly with WP Statistics enabled on a clean WP install (tested here via a simple HTML block):
https://wpstatistics.verdensbedstekunder.dk/However, in my main/production projects, I’m still running into issues – specifically, my listener on replaceState doesn’t fire consistently.
What I’ve observed:
My custom script loads after tracker.js, usingtype="module".Even when I load it before tracker.js (and not as a module), I still see the same problem.My script wraps
history.replaceStateto dispatch a custom or native-like event – this works at first, but stops working after WP Statistics runs.It looks like tracker.js calls
WpStatisticsUserTracker.init(), which in turn callstrackUrlChange()— and each time this happens, it does:history.replaceState = function() {
originalReplaceState.apply(history, arguments);
handleUrlChange();
}That seems to overwrite my wrapped replaceState, which explains why my event handler no longer fires.
A fix that works in my production sites
If I use this script at the very top of my scripts, then my original history.replaceState callbacks works (again)
(function trapHistoryMethods() {
const wrap = (type) => {
let current = history[type];
Object.defineProperty(history, type, {
configurable: true,
get() {
return current;
},
set(fn) {
if(typeof fn !== 'function') {
current = fn;
return;
}
// Wrap new setter
current = function (...args) {
const result = fn.apply(this, args);
const event = new Event(type);
event.arguments = args;
window.dispatchEvent(event);
return result;
};
console.log(✅ Intercepted history.${type} setter);
}
});
// Immediately wrap the current version
history[type] = history[type];
};
wrap('replaceState');
wrap('pushState');
})();/**
* Test Script - URL Search Params Buttons
*
* This script creates 3 buttons that update URL search parameters
* using history.replaceState to help test WP Statistics interference.
*/
(function() {
// Function to safely update URL search params
function updateSearchParams(paramName, paramValue) {
// Get current URL and create URL object for easy param manipulation
const currentUrl = new URL(window.location.href);
// Update the search parameter
currentUrl.searchParams.set(paramName, paramValue);
// Use replaceState to update the URL without reloading the page
try {
window.history.replaceState({}, '', currentUrl.toString());
console.log(URL updated with ${paramName}=${paramValue});
} catch (e) {
console.error('Error updating URL:', e);
}
}
// Function to create a styled button
function createButton(text, paramName, paramValue) {
const button = document.createElement('button');
button.textContent = text;
button.style.margin = '5px';
button.style.padding = '8px 16px';
button.style.backgroundColor = '#4CAF50';
button.style.color = 'white';
button.style.border = 'none';
button.style.borderRadius = '4px';
button.style.cursor = 'pointer';
// Add click handler to update URL params
button.addEventListener('click', () => {
updateSearchParams(paramName, paramValue);
});
return button;
}
// Create container for buttons
function createButtonContainer() {
const container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '20px';
container.style.right = '20px';
container.style.backgroundColor = '#f8f9fa';
container.style.padding = '10px';
container.style.borderRadius = '8px';
container.style.boxShadow = '0 2px 10px rgba(0,0,0,0.2)';
container.style.zIndex = '9999';
// Add title to container
const title = document.createElement('div');
title.textContent = 'URL Param Test Buttons';
title.style.fontWeight = 'bold';
title.style.marginBottom = '10px';
title.style.borderBottom = '1px solid #ddd';
title.style.paddingBottom = '5px';
container.appendChild(title);
return container;
}
// Create and add event monitor to show when replaceState events are triggered
function createEventMonitor(container) {
const monitor = document.createElement('div');
monitor.style.fontSize = '12px';
monitor.style.marginTop = '10px';
monitor.style.padding = '5px';
monitor.style.backgroundColor = '#f0f0f0';
monitor.style.borderRadius = '4px';
monitor.style.maxHeight = '100px';
monitor.style.overflow = 'auto';
monitor.textContent = 'Event Monitor: Waiting for events...';
// Add event listeners to detect history method calls
window.addEventListener('replaceState', () => {
const time = new Date().toLocaleTimeString();
monitor.textContent =${time}: replaceState event detected!;
monitor.style.color = 'green';
// Reset after 3 seconds
setTimeout(() => {
monitor.style.color = 'black';
}, 3000);
});
container.appendChild(monitor);
return monitor;
}
// Function to initialize everything when DOM is ready
function init() {
// Create button container
const container = createButtonContainer();
// Create buttons
const button1 = createButton('Set Filter: A', 'filter', 'option_a');
const button2 = createButton('Set Filter: B', 'filter', 'option_b');
const button3 = createButton('Set Page: 2', 'page', '2');
// Add buttons to container
container.appendChild(button1);
container.appendChild(button2);
container.appendChild(button3);
// Create and add event monitor
createEventMonitor(container);
// Add container to body
document.body.appendChild(container);
console.log('Test buttons added to the page');
}
// Wait for DOM to be ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();Hi Mostafa. I can’t share my actual code, but I asked AI to generate some custom code that basically resemble the issues I’m having. 🙂
If I have WP Statistics enabled, thenwindow.addEventListener('replaceState', () => {})is never called. 🙂I have made a workaround where I dispatch a custom event that I use instead of the “replaceState”, but I wonder if there are any plans to change the behaviour of tracker.js ?
const originalHandleUrlChange = window.WpStatisticsUserTracker.handleUrlChange;window.WpStatisticsUserTracker.handleUrlChange = function () {
originalHandleUrlChange.apply(this, arguments);
const searchParams = new URLSearchParams(window.location.search);
const customEvent = new CustomEvent(“WP_STATISTICS_REPLACE_STATE_FIX”, {
detail: {
searchParams: searchParams.toString(),
url: window.location.href
}
});
window.dispatchEvent(customEvent);
};window.addEventListener(‘WP_STATISTICS_REPLACE_STATE_FIX’, () => {})