can i be notified of cookie changes in client side javascript

后端 未结 6 1121
梦毁少年i
梦毁少年i 2020-12-03 01:21

Can I somehow follow changes to cookies (for my domain) in my client side javascript. For example a function that gets called if a cookie gets changed , deleted or added

相关标签:
6条回答
  • 2020-12-03 01:47

    If the code that manipulated the cookies is yours, you can use localStorage for tracking changed with events. for example, you can store a junk on the localStorage to trigger an event on the other tabs.

    for example

    var checkCookie = function() {
    
    var lastCookies = document.cookie.split( ';' ).map( function( x ) { return x.trim().split( /(=)/ ); } ).reduce( function( a, b ) { 
            a[ b[ 0 ] ] = a[ b[ 0 ] ] ? a[ b[ 0 ] ] + ', ' + b.slice( 2 ).join( '' ) :  
            b.slice( 2 ).join( '' ); return a; }, {} );
    
    
    return function() {
    
        var currentCookies =  document.cookie.split( ';' ).map( function( x ) { return x.trim().split( /(=)/ ); } ).reduce( function( a, b ) { 
            a[ b[ 0 ] ] = a[ b[ 0 ] ] ? a[ b[ 0 ] ] + ', ' + b.slice( 2 ).join( '' ) :  
            b.slice( 2 ).join( '' ); return a; }, {} );
    
    
        for(cookie in currentCookies) {
            if  ( currentCookies[cookie] != lastCookies[cookie] ) {
                console.log("--------")
                console.log(cookie+"="+lastCookies[cookie])
                console.log(cookie+"="+currentCookies[cookie])
            }
    
        }
        lastCookies = currentCookies;
    
    };
    }();
     $(window).on("storage",checkCookie); // via jQuery. can be used also with VanillaJS
    
    
    // on the function changed the cookies
    
    document.cookie = ....
    window.localStorage["1"] = new Date().getTime(); // this will trigger the "storage" event in the other tabs.
    
    0 讨论(0)
  • 2020-12-03 02:03

    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

    (()=> {
      const channel = new BroadcastChannel('cookie-channel');
      channel.onmessage = (e)=> { // get notification from other same-origin tabs/frames
        document.dispatchEvent(new CustomEvent('cookiechange', {
          detail: e.data
        }));
      };
    
      let expando = '_cookie';
      let lastCookie = document.cookie;
      let checkCookieChange = ()=> {
        let cookie = document[expando];
        if (cookie !== lastCookie) {
          try {
            let detail = {oldValue: lastCookie, newValue: cookie};
            document.dispatchEvent(new CustomEvent('cookiechange', {
              detail: detail
            }));
            channel.postMessage(detail); // notify other same-origin tabs/frames
          } finally {
            lastCookie = cookie;
          }
        }
      };
    
      let nativeCookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
      Object.defineProperty(Document.prototype, expando, nativeCookieDesc);
      Object.defineProperty(Document.prototype, 'cookie', { // redefine document.cookie
        enumerable: true,
        configurable: true,
        get() {
          return this[expando];
        },
        set(value) {
          this[expando] = value;
          checkCookieChange();
        }
      });
    })();
    

    Usage

    document.addEventListener('cookiechange', ({detail: {oldValue, newValue}})=> {
      console.log(`Cookie changed from "${oldValue}" to "${newValue}"`);
    });
    
    document.cookie = 'a=1';
    

    Conclusion

    | Metric \ Method  | Periodic Polling            | API Interception |
    | ---------------- | --------------------------- | ---------------- |
    | delay            | depends on polling interval | instant          |
    | scope            | same-domain                 | same-origin      |
    
    0 讨论(0)
  • 2020-12-03 02:05

    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
    0 讨论(0)
  • 2020-12-03 02:07

    We can override document.cookie and watch for cookie changes:

    const parseCookies = (cookies = document.cookie) => cookies.split(/; (.*)/).slice(0, -1).map(cookie => {
        const [name, value] = cookie.split("=")
        return [name, decodeURIComponent(value)]
    })
    
    const COOKIE = Symbol("Cookie")
    let lastCookies = document.cookie
    let lastCookiesParsed = new Map(parseCookies(lastCookies))
    
    Object.defineProperty(Document.prototype, COOKIE, Object.getOwnPropertyDescriptor(Document.prototype, "cookie"))
    
    Object.defineProperty(Document.prototype, "cookie", {
        enumerable: true,
        configurable: true,
        get() {
            return this[COOKIE]
        },
        set(value) {
            this[COOKIE] = value
    
            if (value === lastCookies) {
                return
            }
    
            for (const [name, cookieValue] of parseCookies(value).filter(([name, cookieValue]) => lastCookiesParsed.get(name) === cookieValue)) {
                document.dispatchEvent(new CustomEvent("cookiechange", {
                    detail: {
                        name,
                        value: cookieValue
                    }
                }));
            }
    
            lastCookies = value
            lastCookiesParsed = new Map(parseCookies(lastCookies))
        }
    })
    

    Usage:

    document.addEventListener("cookiechange", ({detail}) => {
        const {name, value} = detail
        console.log(`${name} was set to ${value}`)
    })
    
    0 讨论(0)
  • 2020-12-03 02:09

    Slightly improved (shows a console.log for each changed cookie):

    var checkCookie = function() {
    
    var lastCookies = document.cookie.split( ';' ).map( function( x ) { return x.trim().split( /(=)/ ); } ).reduce( function( a, b ) { 
            a[ b[ 0 ] ] = a[ b[ 0 ] ] ? a[ b[ 0 ] ] + ', ' + b.slice( 2 ).join( '' ) :  
            b.slice( 2 ).join( '' ); return a; }, {} );
    
    
    return function() {
    
        var currentCookies =  document.cookie.split( ';' ).map( function( x ) { return x.trim().split( /(=)/ ); } ).reduce( function( a, b ) { 
            a[ b[ 0 ] ] = a[ b[ 0 ] ] ? a[ b[ 0 ] ] + ', ' + b.slice( 2 ).join( '' ) :  
            b.slice( 2 ).join( '' ); return a; }, {} );
    
    
        for(cookie in currentCookies) {
            if  ( currentCookies[cookie] != lastCookies[cookie] ) {
                console.log("--------")
                console.log(cookie+"="+lastCookies[cookie])
                console.log(cookie+"="+currentCookies[cookie])
            }
    
        }
        lastCookies = currentCookies;
    
    };
    }();
    
    window.setInterval(checkCookie, 100);
    
    0 讨论(0)
  • 2020-12-03 02:12

    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 */
      }
    });
    
    0 讨论(0)
提交回复
热议问题