【问题标题】:Any way to identify browser tab in JavaScript?有什么方法可以在 JavaScript 中识别浏览器选项卡?
【发布时间】:2024-01-14 21:50:01
【问题描述】:

我需要能够识别我在浏览器中的哪个选项卡。我不能从浏览器中获得一些信息来识别标签吗?我不需要知道任何其他选项卡的任何信息,我只需要我所在选项卡的 id。它可以是随机数或序列号,也可以是日期时间戳,只要它在标签的寿命。

我有一个客户端应用程序,它通过 HTTP 连接到远程服务器,如果我在多个选项卡中打开它,每个实例都需要自己的唯一 ID,否则应用程序会失败,所以我只需要一些唯一的数字只要该选项卡存在,它就与该选项卡相关联(即,当我浏览提供此应用程序的网站时,页面刷新后仍然存在的东西)。似乎很简单,应该只在浏览器中可用,例如 window.tabId - 这就是我所需要的。我有一个严重的开发障碍,因为我无法通过这个看似不存在的简单、简单、简单的解决方案。一定有办法(跨浏览器的解决方案)。

有什么想法吗?

【问题讨论】:

    标签: javascript browser tabs uniqueidentifier


    【解决方案1】:

    SessionStorage 是每个选项卡/窗口的,因此您可以在 sessionStorage 中定义一个随机数,如果存在则首先获取它:

    var tabID = sessionStorage.tabID ? 
                sessionStorage.tabID : 
                sessionStorage.tabID = Math.random();
    

    更新
    在某些情况下,您可能在多个选项卡中拥有相同的 sessionStorage(例如,当您复制选项卡时)。在这种情况下,以下代码可能会有所帮助:

    var tabID = sessionStorage.tabID && 
                sessionStorage.closedLastTab !== '2' ? 
                sessionStorage.tabID : 
                sessionStorage.tabID = Math.random();
    sessionStorage.closedLastTab = '2';
    $(window).on('unload beforeunload', function() {
          sessionStorage.closedLastTab = '1';
    });
    

    【讨论】:

    • 这段代码有很大帮助,但是复制窗口时数据存储仍然存在
    • 会话存储不在IE中。
    • 随机选择数字并不能确保它们是唯一的(即选择一个介于 0 和 99 之间的随机数,并且可以多次选择 11)。为什么不是 localStorage 中的序列号?
    • @MaxWaterman,是的,随机数可能会发生冲突,但 Math.random 返回双精度数,其大小为 64 位。所以还不错。 Math.random百万 次运行中生成两个重复数字的概率约为 1%。请参阅此答案*.com/a/28220928/3167374。在新版本的浏览器中,Math.random 的质量似乎更好。现在 f.ex.它是 Chrome 内部使用的 128 位生成器 (v8.dev/blog/math-random)。 (不确定它对碰撞概率的影响有多大。)
    • 对于那些担心重复 id 的人 - 只需为随机生成的 id 使用基于时间的前缀。
    【解决方案2】:

    您必须使用 html5,但 sessionStorage 结合随机 guid 似乎是您想要的。

    【讨论】:

    • 数字可以出现两次,即使它们是随机抽取的,不是吗?因此,两个选项卡有可能具有相同的编号。我原以为 localStorage 中的序列号会更好 - 当您在新选项卡中打开页面时,增加编号并将其存储在 sessionStorage 中。
    • @MaxWaterman:不同的浏览器,私人标签,改变时间......序列并不比随机更有保证。有多种方法可以创建一个尽可能好的 guid - 碰撞的机会很小,以至于不值得打扰。
    【解决方案3】:

    由于我没有找到像 windows.currentTabIndex 这样的简单 Javascript 函数,因此我编写了几行 Javascript 来在每个浏览器选项卡加载时修复它们的 ID。

    function defineTabID()
        {
        var iPageTabID = sessionStorage.getItem("tabID");
          // if it is the first time that this page is loaded
        if (iPageTabID == null)
            {
            var iLocalTabID = localStorage.getItem("tabID");
              // if tabID is not yet defined in localStorage it is initialized to 1
              // else tabId counter is increment by 1
            var iPageTabID = (iLocalTabID == null) ? 1 : Number(iLocalTabID) + 1;
              // new computed value are saved in localStorage and in sessionStorage
            localStorage.setItem("tabID",iPageTabID);
            sessionStorage.setItem("tabID",iPageTabID);
            }
        }
    

    此代码将最后一个标签的索引保存在 localStorage 中以更新新标签的当前值,并保存在 sessionStorage 中以修复页面的 id!

    我必须在我的应用程序的每个页面中调用defineTabId() 函数。由于我使用 JSF 模板进行开发,因此仅在一个地方定义:-)

    【讨论】:

    • 但是如果这段代码在两个同时打开的标签页中运行呢?在我看来,getItem - setItem 调用附近可能存在竞争条件。
    • 如果 2 个不同的代码同时执行此代码,则程序错误。但在我的情况下,不可能在 2 个并行代码中生成多个选项卡,因为创建新选项卡与人工操作相关联,一次只能打开一个选项卡。
    • 如果由于某种原因可以打开多个标签,我的函数必须以不同的方式调用。例如,必须在创建其他选项卡的选项卡之前调用此函数,以便在同一线程中顺序创建多个选项卡!
    【解决方案4】:

    这是一种在页面加载后使 tabId 可用的方法 - 即使在刷新后也具有相同的 tabId

    这也解决了重复选项卡的问题,因为 tabId 已从 sessionStorage 中清除。

    window.addEventListener("beforeunload", function (e)
    {
        window.sessionStorage.tabId = window.tabId;
    
        return null;
    });
    
    window.addEventListener("load", function (e)
    {
        if (window.sessionStorage.tabId)
        {
            window.tabId = window.sessionStorage.tabId;
            window.sessionStorage.removeItem("tabId");
        }
        else
        {
            window.tabId = Math.floor(Math.random() * 1000000);
        }
    
        return null;
    });
    

    使用window.tabId 访问tabId

    需要beforeunload 事件才能使tabId 在刷新后具有相同的可用ID。

    load 事件需要:

    1. 生成初始tabId
    2. 刷新后加载相同的tabId - 如果存在。

    * 我真的不知道为什么我应该做return null 或返回。只需阅读不返回可能会导致函数无法工作:(

    【讨论】:

    • 是否需要等待加载事件?为什么不在脚本执行时同步设置tabId?是否有浏览器不执行重复选项卡中的脚本但仍触发加载事件?
    【解决方案5】:

    您无法使用 javascript 获取标签 ID,但是有一些解决方案可以帮助您在用户打开新标签时使用不同的会话/cookie。

    一些参考:

    Get Browser Tab Index/Id

    get urls of firefox tabs from firefox extension

    How to differ sessions in browser-tabs?

    Get a unique session in each browser tab

    asp.net - session - multiple browser tabs - different sessions?

    【讨论】:

      【解决方案6】:

      对于这里使用 React 的任何人,我做了一个鬼鬼祟祟的小钩子:

      import {useEffect, useMemo} from 'react'
      import uniqid from 'uniqid'
      
      export const TAB_ID_KEY = 'tabId'
      
      export default () => {
        const id = useMemo(uniqid, [])
      
        useEffect(() => {
          if (typeof Storage !== 'undefined') {
            sessionStorage.setItem(TAB_ID_KEY, id)
          }
        }, [id])
      
        return id
      }
      

      把它放在你的基础应用/页面组件上,或者你知道会一直挂载的地方。

      它会在挂载后运行效果,并为会话存储中的当前选项卡设置一个唯一的 id,该选项卡是特定于选项卡的存储。

      复制标签会给你一个新标签的新ID,因为组件将再次挂载并且效果将运行,覆盖前一个标签的ID

      【讨论】:

      • 那么,如果您永远不会读取它,那么将 id 放入 sessionStorage 有什么意义?此外,uniqid 的文档建议它回退到仅在浏览器中使用时间,因此使用简单的Date.now() 可以达到我认为的效果。
      【解决方案7】:

      一种可能的解决方案是使用“window.name”属性,但我不想使用它,因为其他人可以使用它。

      我找到了另一种可能的解决方案:使用 sessionStorage。 FF3.5+、Chrome4+、Safari4+、Opera10.5+、IE8+支持。

      【讨论】:

        【解决方案8】:

        如果用户在 2 毫秒内打开 3 个选项卡的可能性不大,则可以使用时间戳并将其存储到 window.name 中,并将其用作查找中的键。 window.name 值将保留在选项卡中,直到它关闭。

        Timestamp

        【讨论】:

          【解决方案9】:

          您可以在 Service Worker 的“获取”事件处理程序中获取选项卡 ID(或 clientId)。 如果您需要将该 clientId 发送到您的页面,只需使用 client.postMessage()。

          在您的服务人员中:

          self.addEventListener("fetch", function(event) {
           event.waitUntil(async function() {
          
            if (!event.clientId) return;
          
            const client = await clients.get(event.clientId);
          
            if (!client) return;
          
            client.postMessage({
             clientId: event.clientId,
            });
          
           }());
          });
          

          在您的页面脚本中:

          navigator.serviceWorker.addEventListener('message', event => {
           console.log(event.data.clientId);
          });
          

          来源: Client.postMessage()

          【讨论】:

          • 据我了解,客户端id 不会在标签刷新之间保留并生成一个新的,对吗?我根据我在 macOS Chrome 91.0.4472.114 上的经验 testing with this demo 进行了此评估。注意:演示使用postMessage() 到 SW 来识别客户端而不是 Fetch API,但我认为没有区别。
          【解决方案10】:

          至少对于 chrome,自 2013 年以来就有一个官方答案:chrome.tabs API

          【讨论】:

          • 仅用于扩展。