【问题标题】:Check if my website is open in another tab检查我的网站是否在另一个选项卡中打开
【发布时间】:2014-07-04 15:13:24
【问题描述】:

我想用 JavaScript 检查用户是否已经在他们浏览器的另一个标签页中打开了我的网站。

看来我不能用pagevisibility...

我看到的唯一方法是使用基于会话 cookie 的 WebSocket,并检查客户端是否有多个套接字。但是通过这种方式,从当前选项卡中,我必须询问我的服务器该用户是否在其当前浏览器选项卡旁边打开了一个选项卡。有点牵强!

也许localstorage

【问题讨论】:

  • 如果他们打开了另一个标签页,你想做什么?显示一条消息说它不是主窗口?
  • 如果用户在另一个浏览器实例中而不是在当前浏览器实例的另一个选项卡中打开您的网站怎么办?
  • localStorage 或 cookie 应该可以工作,假设它是同一个浏览器。您具体需要做什么?
  • 两个原因: - 使用 facebook 打开许多标签,当您收到聊天消息时,您会听到多次哔哔声。我想防止这种情况。 - 我想将一些数据从一个选项卡转移到其他选项卡。
  • 作为评论,如果您使用 websockets,您可以绕过所有这些浏览器内容并“轻松”识别多个标签

标签: javascript html websocket


【解决方案1】:

使用本地存储,我创建了一个简单的演示,应该可以完成您想做的事情。基本上,它只是维护当前打开的窗口的计数。当窗口关闭时,卸载事件会触发并将其从总窗口计数中删除。

当您第一次看到它时,您可能会认为发生的事情比实际情况要多。其中大部分是尝试将逻辑添加到谁是“主”窗口,以及在您关闭孩子时谁应该接管作为“主”窗口。 (因此 setTimeout 调用以重新检查它是否应该被提升到主窗口) 经过一番摸索后,我认为实施起来需要太多时间,并且超出了这个问题的范围。但是,如果您打开了两个窗口(主窗口和子窗口)并关闭主窗口,则子窗口将被提升为主窗口。

在大多数情况下,您应该能够大致了解正在发生的事情并将其用于您自己的实施。

在此处查看所有操作: http://jsbin.com/mipanuro/1/edit

哦,是的,要实际看到它的实际效果...在多个窗口中打开链接。 :)

更新:

我已经进行了必要的更改以使本地存储保持“主”窗口。当您关闭选项卡时,子窗口可以升级为主窗口。有两种方法可以通过传递给 WindowStateManager 的构造函数的参数来控制“主”窗口状态。这个实现比我之前的尝试要好得多。

JavaScript:

// noprotect

var statusWindow = document.getElementById('status');

(function (win)
{
    //Private variables
    var _LOCALSTORAGE_KEY = 'WINDOW_VALIDATION';
    var RECHECK_WINDOW_DELAY_MS = 100;
    var _initialized = false;
    var _isMainWindow = false;
    var _unloaded = false;
    var _windowArray;
    var _windowId;
    var _isNewWindowPromotedToMain = false;
    var _onWindowUpdated;

    
    function WindowStateManager(isNewWindowPromotedToMain, onWindowUpdated)
    {
        //this.resetWindows();
        _onWindowUpdated = onWindowUpdated;
        _isNewWindowPromotedToMain = isNewWindowPromotedToMain;
        _windowId = Date.now().toString();

        bindUnload();

        determineWindowState.call(this);

        _initialized = true;

        _onWindowUpdated.call(this);
    }

    //Determine the state of the window 
    //If its a main or child window
    function determineWindowState()
    {
        var self = this;
        var _previousState = _isMainWindow;

        _windowArray = localStorage.getItem(_LOCALSTORAGE_KEY);

        if (_windowArray === null || _windowArray === "NaN")
        {
            _windowArray = [];
        }
        else
        {
            _windowArray = JSON.parse(_windowArray);
        }

        if (_initialized)
        {
            //Determine if this window should be promoted
            if (_windowArray.length <= 1 ||
               (_isNewWindowPromotedToMain ? _windowArray[_windowArray.length - 1] : _windowArray[0]) === _windowId)
            {
                _isMainWindow = true;
            }
            else
            {
                _isMainWindow = false;
            }
        }
        else
        {
            if (_windowArray.length === 0)
            {
                _isMainWindow = true;
                _windowArray[0] = _windowId;
                localStorage.setItem(_LOCALSTORAGE_KEY, JSON.stringify(_windowArray));
            }
            else
            {
                _isMainWindow = false;
                _windowArray.push(_windowId);
                localStorage.setItem(_LOCALSTORAGE_KEY, JSON.stringify(_windowArray));
            }
        }

        //If the window state has been updated invoke callback
        if (_previousState !== _isMainWindow)
        {
            _onWindowUpdated.call(this);
        }

        //Perform a recheck of the window on a delay
        setTimeout(function()
                   {
                     determineWindowState.call(self);
                   }, RECHECK_WINDOW_DELAY_MS);
    }

    //Remove the window from the global count
    function removeWindow()
    {
        var __windowArray = JSON.parse(localStorage.getItem(_LOCALSTORAGE_KEY));
        for (var i = 0, length = __windowArray.length; i < length; i++)
        {
            if (__windowArray[i] === _windowId)
            {
                __windowArray.splice(i, 1);
                break;
            }
        }
        //Update the local storage with the new array
        localStorage.setItem(_LOCALSTORAGE_KEY, JSON.stringify(__windowArray));
    }

    //Bind unloading events  
    function bindUnload()
    {
        win.addEventListener('beforeunload', function ()
        {
            if (!_unloaded)
            {
                removeWindow();
            }
        });
        win.addEventListener('unload', function ()
        {
            if (!_unloaded)
            {
                removeWindow();
            }
        });
    }

    WindowStateManager.prototype.isMainWindow = function ()
    {
        return _isMainWindow;
    };

    WindowStateManager.prototype.resetWindows = function ()
    {
        localStorage.removeItem(_LOCALSTORAGE_KEY);
    };

    win.WindowStateManager = WindowStateManager;
})(window);

var WindowStateManager = new WindowStateManager(false, windowUpdated);

function windowUpdated()
{
    //"this" is a reference to the WindowStateManager
    statusWindow.className = (this.isMainWindow() ? 'main' : 'child');
}
//Resets the count in case something goes wrong in code
//WindowStateManager.resetWindows()

HTML:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
  <div id='status'> 
    <span class='mainWindow'>Main Window</span>
    <span class='childWindow'>Child Window</span>
  </div>
</body>
</html>

CSS:

#status
{
  display:table;
  width:100%;
  height:500px;
  border:1px solid black;
}
span
{
  vertical-align:middle;
  text-align:center; 
  margin:0 auto;
  font-size:50px;
  font-family:arial;
  color:#ba3fa3;  
  display:none;
}

#status.main .mainWindow,
#status.child .childWindow
{
  display:table-cell;
}

.mainWindow
{
  background-color:#22d86e;
}
.childWindow
{
  background-color:#70aeff;
}

【讨论】:

  • 为什么需要知道哪个选项卡是“主”选项卡,哪些是“子”选项卡?
【解决方案2】:

带有localStorage 和存储侦听器的较短版本

<script type="text/javascript">
        // Broadcast that you're opening a page.
        localStorage.openpages = Date.now();
        var onLocalStorageEvent = function(e){
            if(e.key == "openpages"){
                // Listen if anybody else is opening the same page!
                localStorage.page_available = Date.now();
            }
            if(e.key == "page_available"){
                alert("One more page already open");
            }
        };
        window.addEventListener('storage', onLocalStorageEvent, false);
</script>

更新:

  • 也适用于页面崩溃。
  • 在 chrome 中刺激页面崩溃:chrome://inducebrowsercrashforrealz

Live demo

【讨论】:

  • 如果浏览器崩溃或发生其他意外情况,这是否有效?
  • @hugoderhungrige 浏览器崩溃不会删除任何本地存储对象,所以它应该仍然可以工作
  • 这就是我的意思。如果设置了 openpages,页面崩溃并且您重新加载浏览器,您会错误地收到警报还是我弄错了?
  • @SasiVarunan 在 IOS Safari 13.7 上,我还会在第一页加载时收到警报。似乎事件侦听器在第一个 localStorage.openpages = Date.now() 上触发,即使它已经过去了。将事件侦听器放入 setTimeout(function()=>{}, 0) 使其等待足够长的时间才能工作。我确实认为,如果同时打开 2 个选项卡,这可能会给它一个可能但不太可能错过该事件的机会。
【解决方案3】:

(2021 年)

const bc = new BroadcastChannel("my-awesome-site");

bc.onmessage = (event) => {
  if (event.data === "Am I the first?") {
    bc.postMessage(`No you're not.`);
    alert("Another tab of this site just got opened");
  }
  if (event.data === `No you're not.`) {
    alert("An instance of this site is already running");
  }
};

bc.postMessage('Am I the first?');

粘贴到您的代码库中并通过打开 2 个选项卡对其进行测试。

【讨论】:

    【解决方案4】:

    我知道已经晚了,但也许可以帮助某人

    这段代码的sn-p,将检测有多少标签是打开的,有多少是活动的(可见的),如果没有一个标签是活动的,它将选择最后打开的标签,作为活动的。

    此代码也将处理 windows/tab 崩溃,并在崩溃时刷新计数。

    由于Stakoverflow目前不支持localStorage,请测试here

    <html>
    <body>
    Open in several tabs or windows
    <div id="holder_element"></div>
    
    <script type="text/javascript">
        //localStorage.clear();
        manage_crash();
    
        //Create a windows ID for each windows that is oppened
        var current_window_id = Date.now() + "";//convert to string
        var time_period = 3000;//ms
    
        //Check to see if PageVisibility API is supported or not
        var PV_API = page_visibility_API_check();
    
        /************************
        ** PAGE VISIBILITY API **
        *************************/
        function page_visibility_API_check ()
        {
            var page_visibility_API = false;
            var visibility_change_handler = false;
            if ('hidden' in document)
            {
                page_visibility_API = 'hidden';
                visibility_change_handler = 'visibilitychange';
            }
            else
            {
                var prefixes = ['webkit','moz','ms','o'];
                //loop over all the known prefixes
                for (var i = 0; i < prefixes.length; i++){
                    if ((prefixes[i] + 'Hidden') in document)
                    {
                        page_visibility_API = prefixes[i] + 'Hidden';
                        visibility_change_handler = prefixes[i] + 'visibilitychange';
                    }
                }
            }
    
            if (!page_visibility_API)
            {
                //PageVisibility API is not supported in this device
                return page_visibility_API;
            }
    
            return {"hidden": page_visibility_API, "handler": visibility_change_handler};
        }
    
        if (PV_API)
        {
            document.addEventListener(PV_API.handler, function(){
                //console.log("current_window_id", current_window_id, "document[PV_API.hidden]", document[PV_API.hidden]);
                if (document[PV_API.hidden])
                {
                    //windows is hidden now
                    remove_from_active_windows(current_window_id);
                    //skip_once = true;
                }
                else
                {
                    //windows is visible now
                    //add_to_active_windows(current_window_id);
                    //skip_once = false;
                    check_current_window_status ();
                }
            }, false);
        }
    
        /********************************************
        ** ADD CURRENT WINDOW TO main_windows LIST **
        *********************************************/
        add_to_main_windows_list(current_window_id);
        //update active_window to current window
        localStorage.active_window = current_window_id;
    
        /**************************************************************************
        ** REMOVE CURRENT WINDOWS FROM THE main_windows LIST ON CLOSE OR REFRESH **
        ***************************************************************************/
        window.addEventListener('beforeunload', function ()
        {
            remove_from_main_windows_list(current_window_id);
        });
    
        /*****************************
        ** ADD TO main_windows LIST **
        ******************************/
        function add_to_main_windows_list(window_id)
        {
            var temp_main_windows_list = get_main_windows_list();
            var index = temp_main_windows_list.indexOf(window_id);
    
            if (index < 0)
            {
                //this windows is not in the list currently
                temp_main_windows_list.push(window_id);
            }
    
            localStorage.main_windows = temp_main_windows_list.join(",");
    
            return temp_main_windows_list;
        }
    
        /**************************
        ** GET main_windows LIST **
        ***************************/
        function get_main_windows_list()
        {
            var temp_main_windows_list = [];
            if (localStorage.main_windows)
            {
                temp_main_windows_list = (localStorage.main_windows).split(",");
            }
    
            return temp_main_windows_list;
        }
    
        /**********************************************
        ** REMOVE WINDOWS FROM THE main_windows LIST **
        ***********************************************/
        function remove_from_main_windows_list(window_id)
        {
            var temp_main_windows_list = [];
            if (localStorage.main_windows)
            {
                temp_main_windows_list = (localStorage.main_windows).split(",");
            }
    
            var index = temp_main_windows_list.indexOf(window_id);
            if (index > -1) {
                temp_main_windows_list.splice(index, 1);
            }
    
            localStorage.main_windows = temp_main_windows_list.join(",");
    
            //remove from active windows too
            remove_from_active_windows(window_id);
    
            return temp_main_windows_list;
        }
    
        /**************************
        ** GET active_windows LIST **
        ***************************/
        function get_active_windows_list()
        {
            var temp_active_windows_list = [];
            if (localStorage.actived_windows)
            {
                temp_active_windows_list = (localStorage.actived_windows).split(",");
            }
    
            return temp_active_windows_list;
        }
    
        /*************************************
        ** REMOVE FROM actived_windows LIST **
        **************************************/
        function remove_from_active_windows(window_id)
        {
            var temp_active_windows_list = get_active_windows_list();
    
            var index = temp_active_windows_list.indexOf(window_id);
            if (index > -1) {
                temp_active_windows_list.splice(index, 1);
            }
    
            localStorage.actived_windows = temp_active_windows_list.join(",");
    
            return temp_active_windows_list;
        }
    
        /********************************
        ** ADD TO actived_windows LIST **
        *********************************/
        function add_to_active_windows(window_id)
        {
            var temp_active_windows_list = get_active_windows_list();
    
            var index = temp_active_windows_list.indexOf(window_id);
    
            if (index < 0)
            {
                //this windows is not in active list currently
                temp_active_windows_list.push(window_id);
            }
    
            localStorage.actived_windows = temp_active_windows_list.join(",");
    
            return temp_active_windows_list;
        }
    
        /*****************
        ** MANAGE CRASH **
        ******************/
        //If the last update didn't happened recently (more than time_period*2)
        //we will clear saved localStorage's data and reload the page
        function manage_crash()
        {
            if (localStorage.last_update)
            {
                if (parseInt(localStorage.last_update) + (time_period * 2) < Date.now())
                {
                    //seems a crash came! who knows!?
                    //localStorage.clear();
                    localStorage.removeItem('main_windows');
                    localStorage.removeItem('actived_windows');
                    localStorage.removeItem('active_window');
                    localStorage.removeItem('last_update');
                    location.reload();
                }
            }
        }
    
        /********************************
        ** CHECK CURRENT WINDOW STATUS **
        *********************************/
        function check_current_window_status(test)
        {
            manage_crash();
    
            if (PV_API)
            {
                var active_status = "Inactive";
                var windows_list = get_main_windows_list();
    
                var active_windows_list = get_active_windows_list();
    
                if (windows_list.indexOf(localStorage.active_window) < 0)
                {
                    //last actived windows is not alive anymore!
                    //remove_from_main_windows_list(localStorage.active_window);
    
                    //set the last added window, as active_window
                    localStorage.active_window = windows_list[windows_list.length - 1];
                }
    
                if (! document[PV_API.hidden])
                {
                    //Window's page is visible
                    localStorage.active_window = current_window_id;
                }
    
                if (localStorage.active_window == current_window_id)
                {
                    active_status = "Active";
                }
    
                if (active_status == "Active")
                {
                    active_windows_list = add_to_active_windows(current_window_id);
                }
                else
                {
                    active_windows_list = remove_from_active_windows(current_window_id);
                }
    
                console.log(test, active_windows_list);
    
                var element_holder = document.getElementById("holder_element");
                element_holder.insertAdjacentHTML("afterbegin", "<div>"+element_holder.childElementCount+") Current Windows is "+ active_status +"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"+active_windows_list.length+" window(s) is visible and active of "+ windows_list.length +" windows</div>");
            }
            else
            {
                console.log("PageVisibility API is not supported :(");
                //our INACTIVE pages, will remain INACTIVE forever, you need to make some action in this case!
            }
    
            localStorage.last_update = Date.now();
        }
    
        //check storage continuously
        setInterval(function(){
            check_current_window_status ();
        }, time_period);
    
        //initial check
        check_current_window_status ();
    </script>
    </body>
    </html>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-31
      • 2021-07-30
      • 1970-01-01
      • 2017-01-26
      • 2015-12-04
      • 2017-12-28
      • 1970-01-01
      • 2014-11-10
      相关资源
      最近更新 更多