【问题标题】:Client side script variables scope for multiple opened tabs of browser issue浏览器问题的多个打开选项卡的客户端脚本变量范围
【发布时间】:2014-05-29 06:13:20
【问题描述】:

我的共享 _Layout.cshtml 中有一个 jquery 脚本,它记录最后一次点击并在特定时间间隔后注销用户。出现的问题是,当打开超过 1 个页面时(即在浏览器的多个选项卡中,打开同一个应用程序的不同页面),如果任何页面在指定时间内保持空闲,它就会注销用户。以及用户当前正在处理的另一个页面,当用户单击时,它会引发异常(这很明显,因为用户已被其他页面注销)。我不能使用sessions 相关的东西。无论如何,我可以在浏览器的不同打开选项卡中维护此类变量的范围吗?如果用户正在处理任何打开的选项卡,我想将其他打开的选项卡的计时器重置为零。我试着先四处寻找答案,但我发现每个问题都与我的场景不同。

(我使用的是 MVC 4。Jquery 脚本位于 /Shared/_Layout.cshtml

【问题讨论】:

    标签: jquery asp.net-mvc-4 razor timeout


    【解决方案1】:

    我使用一些名为store.js 的库解决了这个问题。它不需要轮询服务器和其他东西,相反,只要在任何打开的选项卡上设置 cookie,就会立即在选项卡之间共享 cookie(不需要刷新选项卡;这通常是为了获取cookie,在其他选项卡上设置。)。最重要的是,我喜欢的是store.js 在可用时使用localStorage,并在IE6IE7 中使用userData 行为。没有闪光灯来减慢您的页面加载速度。没有 cookie 来增加您的网络请求。 store.js 依赖 JSON 序列化到磁盘。使用起来非常简单。

    使用示例:

    store.set('username', 'joe')
    document.write('username: ' + store.get('username') + '<br />')
    store.remove('username')
    store.clear()
    store.set('user', { name: 'joe', likes: 'javascript' })
    var user = store.get('user')
    document.write(user.name + ' likes ' + user.likes)
    

    【讨论】:

      【解决方案2】:

      空闲计时器可以使用 localStorage 跨多个窗口和选项卡进行通信(保持同步)。在 github 上,marcuswestin/store.js 为多种浏览器提供了良好的功能。这是提供同步窗口/选项卡/iframe 的 idletimer 插件的“测试”代码,前提是它们都在同一个域中。 https://github.com/JillElaine/jquery-idleTimeout

      /**
       * This work is licensed under the MIT License
       *
       * Configurable idle (no activity) timer and logout redirect for jQuery.
       * Works across multiple windows, tabs and iframes from the same domain.
       *
       * Dependencies: JQuery v1.7+, JQuery UI, store.js from https://github.com/marcuswestin/store.js - v1.3.4+
       *
       * Commented and console logged for debugging with Firefox & Firebug or similar
       * version 1.0.6
       **/
      
      /*global jQuery: false, document: false, store: false, clearInterval: false, setInterval: false, setTimeout: false, window: false, alert: false, console: false*/
      /*jslint indent: 2, sloppy: true, plusplus: true*/
      
      (function ($) {
      
        $.fn.idleTimeout = function (options) {
          console.log('start');
          //##############################
          //## Configuration Variables
          //##############################
          var defaults = {
            idleTimeLimit: 30000,         // 30 seconds for testing. 'No activity' time limit in milliseconds. 1200000 = 20 Minutes
            dialogDisplayLimit: 20000,    // 20 seconds for testing. Time to display the warning dialog before redirect (and optional callback) in milliseconds. 180000 = 3 Minutes
            redirectUrl: '/logout',       // redirect to this url on timeout logout. Set to "redirectUrl: false" to disable redirect
      
            // optional custom callback to perform before redirect
            customCallback: false,       // set to false for no customCallback
            // customCallback:    function () {    // define optional custom js function
                // perform custom action before logout
            // },
      
            // configure which activity events to detect
            // http://www.quirksmode.org/dom/events/
            // https://developer.mozilla.org/en-US/docs/Web/Reference/Events
            activityEvents: 'click keypress scroll wheel mousewheel', // separate each event with a space
      
            //dialog box configuration
            dialogTitle: 'Session Expiration Warning',
            dialogText: 'Because you have been inactive, your session is about to expire.',
      
            // server-side session keep-alive timer
            sessionKeepAliveTimer: 600000 // Ping the server at this interval in milliseconds. 600000 = 10 Minutes
            // sessionKeepAliveTimer: false // Set to false to disable pings
          },
      
          //##############################
          //## Private Variables
          //##############################
            opts = $.extend(defaults, options),
            checkHeartbeat = 2000, // frequency to check for timeouts - 2000 = 2 seconds
            origTitle = document.title, // save original browser title
            sessionKeepAliveUrl = window.location.href, // set URL to ping to user's current window
            keepSessionAlive, activityDetector,
            idleTimer, remainingTimer, checkIdleTimeout, idleTimerLastActivity, startIdleTimer, stopIdleTimer,
            openWarningDialog, dialogTimer, checkDialogTimeout, startDialogTimer, stopDialogTimer, isDialogOpen, destroyWarningDialog,
            countdownDisplay, logoutUser,
            checkForIframes, includeIframes, attachEventIframe; // iframe functionality
      
          //##############################
          //## Private Functions
          //##############################
          keepSessionAlive = function () {
      
            if (opts.sessionKeepAliveTimer) {
              var keepSession = function () {
                if (idleTimerLastActivity === store.get('idleTimerLastActivity')) {
                  console.log('keep session alive function');
                  $.get(sessionKeepAliveUrl);
                }
              };
      
              setInterval(keepSession, opts.sessionKeepAliveTimer);
            }
          };
      
          activityDetector = function () {
      
            $('body').on(opts.activityEvents, function () {
      
              if (isDialogOpen() !== true) {
                console.log('activity detected');
                startIdleTimer();
              } else {
                console.log('dialog open. activity ignored');
              }
            });
          };
      
          checkIdleTimeout = function () {
            var timeNow = $.now(), timeIdleTimeout = (store.get('idleTimerLastActivity') + opts.idleTimeLimit);
      
            if (timeNow > timeIdleTimeout) {
              console.log('timeNow: ' + timeNow + ' > idle ' + timeIdleTimeout);
              if (isDialogOpen() !== true) {
                console.log('dialog is not open & will be opened');
                openWarningDialog();
                startDialogTimer();
              }
            } else if (store.get('idleTimerLoggedOut') === true) { //a 'manual' user logout?
              logoutUser();
            } else {
              console.log('idle not yet timed out');
              if (isDialogOpen() === true) {
                console.log('dialog is open & will be closed');
                destroyWarningDialog();
                stopDialogTimer();
              }
            }
          };
      
          startIdleTimer = function () {
            stopIdleTimer();
            idleTimerLastActivity = $.now();
            store.set('idleTimerLastActivity', idleTimerLastActivity);
            console.log('start idle timer: ' + idleTimerLastActivity);
            idleTimer = setInterval(checkIdleTimeout, checkHeartbeat);
          };
      
          stopIdleTimer = function () {
            clearInterval(idleTimer);
          };
      
          openWarningDialog = function () {
            var dialogContent = "<div id='idletimer_warning_dialog'><p>" + opts.dialogText + "</p><p style='display:inline'>Time remaining: <div style='display:inline' id='countdownDisplay'></div></p></div>";
      
            $(dialogContent).dialog({
              buttons: {
                "Stay Logged In": function () {
                  console.log('Stay Logged In button clicked');
                  destroyWarningDialog();
                  stopDialogTimer();
                  startIdleTimer();
                },
                "Log Out Now": function () {
                  console.log('Log Out Now button clicked');
                  logoutUser();
                }
              },
              closeOnEscape: false,
              modal: true,
              title: opts.dialogTitle
            });
      
            // hide the dialog's upper right corner "x" close button
            $('.ui-dialog-titlebar-close').css('display', 'none');
      
            // start the countdown display
            countdownDisplay();
      
            // change title bar to warning message
            document.title = opts.dialogTitle;
          };
      
          checkDialogTimeout = function () {
            var timeNow = $.now(), timeDialogTimeout = (store.get('idleTimerLastActivity') + opts.idleTimeLimit + opts.dialogDisplayLimit);
      
            if ((timeNow > timeDialogTimeout) || (store.get('idleTimerLoggedOut') === true)) {
              console.log('timeNow: ' + timeNow + ' > dialog' + timeDialogTimeout);
              logoutUser();
            } else {
              console.log('dialog not yet timed out');
            }
          };
      
          startDialogTimer = function () {
            dialogTimer = setInterval(checkDialogTimeout, checkHeartbeat);
          };
      
          stopDialogTimer = function () {
            clearInterval(dialogTimer);
            clearInterval(remainingTimer);
          };
      
          isDialogOpen = function () {
            var dialogOpen = $("#idletimer_warning_dialog").is(":visible");
      
            if (dialogOpen === true) {
              return true;
            }
            return false;
          };
      
          destroyWarningDialog = function () {
            console.log('dialog destroyed');
            $(".ui-dialog-content").dialog('destroy').remove();
            document.title = origTitle;
          };
      
          // display remaining time on warning dialog
          countdownDisplay = function () {
            var dialogDisplaySeconds = opts.dialogDisplayLimit / 1000, mins, secs;
      
            remainingTimer = setInterval(function () {
              mins = Math.floor(dialogDisplaySeconds / 60); // minutes
              if (mins < 10) { mins = '0' + mins; }
              secs = dialogDisplaySeconds - (mins * 60); // seconds
              if (secs < 10) { secs = '0' + secs; }
              $('#countdownDisplay').html(mins + ':' + secs);
              dialogDisplaySeconds -= 1;
            }, 1000);
          };
      
          logoutUser = function () {
            console.log('logout function');
            store.set('idleTimerLoggedOut', true);
      
            if (opts.customCallback) {
              console.log('logout function custom callback');
              opts.customCallback();
            }
      
            if (opts.redirectUrl) {
              console.log('logout function redirect to URL');
              window.location.href = opts.redirectUrl;
            }
          };
      
          // document must be in readyState 'complete' before looking for iframes
          checkForIframes = function () {
      
            var docReadyCheck, isDocReady;
      
            docReadyCheck = function () {
              if (document.readyState === "complete") {
                console.log('check for iframes, now that the document is complete');
                clearInterval(isDocReady);
                includeIframes();
              }
            };
      
            isDocReady = setInterval(docReadyCheck, 1000);
          };
      
          // look for iframes
          includeIframes = function () {
            console.log('include iframes start');
      
            var foundIframes = document.getElementsByTagName('iframe'), index, iframeItem;
      
            if (foundIframes.length > 0) { //at least one iframe found
              console.log('iframes found: ' + foundIframes.length);
              // attach events to each iframe found
              for (index = 0; index < foundIframes.length; index++) {
      
                iframeItem = foundIframes.item(index);
      
                if (iframeItem.attachEvent) { // IE < 11. Returns a boolean true/false
                  console.log('attach event to iframe. Browser IE < 11');
                  iframeItem.attachEvent('onload', attachEventIframe(index));
                } else { // IE >= 11 and FF, etc.
                  console.log('attach event to iframe. Browser NOT IE < 11');
                  iframeItem.addEventListener('load', attachEventIframe(index), false);
                }
      
              } // end for loop
      
            } // end if any iframes
          };
      
          // attach events to each iframe
          attachEventIframe = function (index) {
      
            var iframe = $('iframe:eq(' + index + ')').contents().find('html');
      
            iframe.on(opts.activityEvents, function (event) {
              console.log('bubbling iframe activity event to body of page');
              $('body').trigger(event);
            });
          };
      
          //###############################
          // Build & Return the instance of the item as a plugin
          // This is your construct.
          //###############################
          return this.each(function () {
      
            if (store.enabled) {
              idleTimerLastActivity = $.now();
              store.set('idleTimerLastActivity', idleTimerLastActivity);
              store.set('idleTimerLoggedOut', false);
            } else {
              alert('Please disable "Private Mode", or upgrade to a modern browser. Or perhaps a dependent file missing. Please see: https://github.com/marcuswestin/store.js');
            }
      
            activityDetector();
      
            keepSessionAlive();
      
            startIdleTimer();
      
            checkForIframes();
          });
        };
      }(jQuery));
      

      【讨论】:

        猜你喜欢
        • 2019-03-12
        • 1970-01-01
        • 2012-08-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多