【问题标题】:can i be notified of cookie changes in client side javascript我可以在客户端 javascript 中收到有关 cookie 更改的通知吗
【发布时间】:2012-12-29 22:33:55
【问题描述】:

我能否以某种方式在我的客户端 JavaScript 中跟踪对 cookie(对于我的域)的更改。例如,如果 cookie 被更改、删除或添加,则会调用一个函数

按优先顺序排列

  • 标准跨浏览器
  • 跨浏览器
  • 特定于浏览器
  • 扩展/插件

为什么?因为我在窗口/标签 #1 中依赖的 cookie 可以在窗口/标签 #2 中更改。

我发现 chrome 允许在 cookie 更改时通知扩展程序。但这是我最不喜欢的选择

【问题讨论】:

  • Cookie 无法启动与其各自网页的通信。跟踪 cookie 的内容是网页的责任。

标签: javascript cookies


【解决方案1】:

一种选择是编写一个定期检查 cookie 是否有更改的函数:

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
  • 这个例子使用了一个持久内存的闭包。外部函数立即执行,返回内部函数,并创建一个私有作用域。
  • window.setInterval

【讨论】:

  • 似乎是我唯一的便携选项
  • @Worthy7 这看起来像是用于浏览器扩展的 API,而不是网站
  • 不应该是window.setInterval(checkCookie(), 100);
  • @d7my checkCookie 在实例化时立即被调用并返回一个fn,所以不,你不需要在setInterval 中调用它
【解决方案2】:

方法一:定期轮询

投票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);
}

用法

listenCookieChange(({oldValue, newValue})=> {
  console.log(`Cookie changed from "${oldValue}" to "${newValue}"`);
}, 1000);

document.cookie = 'a=1';

方法二:API拦截

拦截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
    }));
  };
})();

用法

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

document.cookie = 'a=1';

备注

  1. 不适用于 IE
  2. Safari 需要 BroadcastChannel polyfill

结论

| Metric \ Method  | Periodic Polling            | API Interception |
| ---------------- | --------------------------- | ---------------- |
| delay            | depends on polling interval | instant          |
| scope            | same-domain                 | same-origin      |

【讨论】:

  • 只有在浏览器控制台中设置 cookie 时才对我有效,但如果 cookie 是由 set-cookie 响应标头设置时则无效
  • 有一个功能齐全的实验性Cookie Store API,wicg.github.io/cookie-store/#example-1ce710fe
  • 不幸的是,目前 Cookie Store API 似乎还不能完全跨浏览器。
【解决方案3】:

我认为我的方式更好。我编写了一个自定义事件来检测 cookie 何时出现:

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 */
  }
});

【讨论】:

  • 感谢您的解决方法! cookieValue: document.cookie 应初始化为 cookieValue: ""。我在本地测试过
【解决方案4】:

如果操作 cookie 的代码是您的,您可以使用 localStorage 跟踪随事件发生的变化。例如,您可以在 localStorage 上存储垃圾以触发其他选项卡上的事件。

例如

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.

【讨论】:

  • 这很有趣 - 使用 localStorage 作为打开同一个“应用程序”的浏览器窗口之间的通信机制
【解决方案5】:

略微改进(显示每个更改的 cookie 的 console.log):

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);

【讨论】:

    【解决方案6】:

    我们可以使用CookieStore API:

    cookieStore.addEventListener('change', ({changed}) => {
        for (const {name, value} of changed) {
            console.log(`${name} was set to ${value}`);
        }
    });
    

    【讨论】:

    【解决方案7】:

    如果你想使用新的CookieStore 并希望得到所有浏览器的支持,你可以安装一个(推测的)polyfill,如下所示:https://github.com/markcellus/cookie-store

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-07
      • 1970-01-01
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多