Проверьте, открыт ли мой сайт на другой вкладке

Я хочу проверить с помощью JavaScript, открыл ли пользователь мой сайт в другой вкладке своего браузера.

Кажется, я не могу сделать это с pagevisibility...

Единственный способ, который я вижу, - это использовать WebSocket на основе куки файла сессии и проверить, есть ли у клиента более одного сокета. Но, таким образом, из текущей вкладки я должен спросить мой сервер, есть ли у этого пользователя вкладка, открытая рядом с его текущей вкладкой браузера. Это немного надумано!

Может быть, с localstorage?

Ответ 1

Используя локальное хранилище, я создал простую демонстрацию, которая должна выполнить то, что вы хотите сделать. В принципе, он просто поддерживает подсчет открытых окон. Когда окно закрывается, выгружаются события и удаляются из общего количества окон.

Когда вы сначала смотрите на него, вы можете думать, что там происходит больше, чем есть на самом деле. Большая часть из них заключалась в попытке добавить логику к тому, кто был "главным" окном, и кто должен стать "основным" окном, когда вы закрыли детей. (Следовательно, setTimeout вызывает повторную проверку, если его следует продвигать в главное окно) После некоторой царапины на голове, я решил, что потребуется слишком много времени для реализации и выходит за рамки этого вопроса. Однако, если у вас открыто два окна (Main и Child), и вы закрываете Main, ребенок будет переведен в основную часть.Забастовкa >

По большей части вы должны иметь возможность получить общее представление о том, что происходит, и использовать его для своей собственной реализации.

Посмотрите все это в действии: 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 и слушателем Storage

<script type="text/javascript">
        // Broad cast that your're opening a page.
        localStorage.openpages = Date.now();
        var onLocalStorageEvent = function(e){
            if(e.key == "openpages"){
                // Listen if anybody else 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 демо

Ответ 3

Я знаю, что уже поздно, но, возможно, помогу кому-нибудь

Этот фрагмент кода определит, сколько открытых вкладок и сколько активных (видимых), и если ни одна из вкладок не активна, он выберет последнюю открытую вкладку как активную.

Этот код также будет обрабатывать сбой окон/вкладок и обновлять счет при сбое.

Поскольку localStorage в настоящее время не поддерживается в Stakoverflow, проверьте здесь.

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