can i be notified of cookie changes in client side javascript
One option is to write a function that periodically checks the cookie for changes:
var checkCookie = function() {
var lastCookie = document.cookie; // 'static' memory between function calls
return function() {
var currentCookie = document.cookie;
if (currentCookie != lastCookie) {
// something useful like parse cookie, run a callback fn, etc.
lastCookie = currentCookie; // store latest cookie
}
};
}();
window.setInterval(checkCookie, 100); // run every 100 ms
- This example uses a closure for persistent memory. The outer function is executed immediately, returning the inner function, and creating a private scope.
- window.setInterval
Method 1: Periodic Polling
Poll document.cookie
function listenCookieChange(callback, interval = 1000) {
let lastCookie = document.cookie;
setInterval(()=> {
let cookie = document.cookie;
if (cookie !== lastCookie) {
try {
callback({oldValue: lastCookie, newValue: cookie});
} finally {
lastCookie = cookie;
}
}
}, interval);
}
Usage
listenCookieChange(({oldValue, newValue})=> {
console.log(`Cookie changed from "${oldValue}" to "${newValue}"`);
}, 1000);
document.cookie = 'a=1';
Method 2: API Interception
Intercept document.cookie
(()=> {
let lastCookie = document.cookie;
// rename document.cookie to document._cookie, and redefine document.cookie
const expando = '_cookie';
let nativeCookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
Object.defineProperty(Document.prototype, expando, nativeCookieDesc);
Object.defineProperty(Document.prototype, 'cookie', {
enumerable: true,
configurable: true,
get() {
return this[expando];
},
set(value) {
this[expando] = value;
// check cookie change
let cookie = this[expando];
if (cookie !== lastCookie) {
try {
// dispatch cookie-change messages to other same-origin tabs/frames
let detail = {oldValue: lastCookie, newValue: cookie};
this.dispatchEvent(new CustomEvent('cookiechange', {
detail: detail
}));
channel.postMessage(detail);
} finally {
lastCookie = cookie;
}
}
}
});
// listen cookie-change messages from other same-origin tabs/frames
const channel = new BroadcastChannel('cookie-channel');
channel.onmessage = (e)=> {
lastCookie = e.data.newValue;
document.dispatchEvent(new CustomEvent('cookiechange', {
detail: e.data
}));
};
})();
Usage
document.addEventListener('cookiechange', ({detail: {oldValue, newValue}})=> {
console.log(`Cookie changed from "${oldValue}" to "${newValue}"`);
});
document.cookie = 'a=1';
Notes
- not for IE
- require BroadcastChannel polyfill for Safari
Conclusion
| Metric \ Method | Periodic Polling | API Interception |
| ---------------- | --------------------------- | ---------------- |
| delay | depends on polling interval | instant |
| scope | same-domain | same-origin |
I think my way is better. I wrote a custom event for detect when cookie is chanced:
const cookieEvent = new CustomEvent("cookieChanged", {
bubbles: true,
detail: {
cookieValue: document.cookie,
checkChange: () => {
if (cookieEvent.detail.cookieValue != document.cookie) {
cookieEvent.detail.cookieValue = document.cookie;
return 1;
} else {
return 0;
}
},
listenCheckChange: () => {
setInterval(function () {
if (cookieEvent.detail.checkChange() == 1) {
cookieEvent.detail.changed = true;
//fire the event
cookieEvent.target.dispatchEvent(cookieEvent);
} else {
cookieEvent.detail.changed = false;
}
}, 1000);
},
changed: false
}
});
/*FIRE cookieEvent EVENT WHEN THE PAGE IS LOADED TO
CHECK IF USER CHANGED THE COOKIE VALUE */
document.addEventListener("DOMContentLoaded", function (e) {
e.target.dispatchEvent(cookieEvent);
});
document.addEventListener("cookieChanged", function (e) {
e.detail.listenCheckChange();
if(e.detail.changed === true ){
/*YOUR CODE HERE FOR DO SOMETHING
WHEN USER CHANGED THE COOKIE VALUE */
}
});