64

I need to be able to identify what tab I am in within the browser. Isn't there some bit of information I can get from the browser to identify the tab? I don't need to know anything about any other tabs, I just need an id for the tab I am in. It could be a random or sequenced number, or a date-time stamp, as long as it remains the same for the life of the tab.

I have a client side app that makes a BOSH over HTTP connection to a remote server, and if I open it in multiple tabs, each instance needs its own unique id or the app fails, so I just need some unique number that is associated with the tab for as long as that tab exists (i.e. something that survives page refresh as I navigate the site that provides this app). Seems like a no-brainer that should just be available in the browser, like window.tabId - that's all I need. I've got a serious development block because I cannot get past this simple, simple, simple solution that doesn't seem to exist. There must be a way (a cross-browser solution at that).

Any ideas?

user1588877
  • 775
  • 1
  • 5
  • 9

12 Answers12

67

SessionStorage is per tab/window, so you can define a random number in sessionStorage and get it at first if exists:

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

UPDATE:
In some cases, you may have same sessionStorage in multiple tabs (e.g. when you duplicate tab). In that case, following code may help:

var tabID = sessionStorage.tabID && 
            sessionStorage.closedLastTab !== '2' ? 
            sessionStorage.tabID : 
            sessionStorage.tabID = Math.random();
sessionStorage.closedLastTab = '2';
$(window).on('unload beforeunload', function() {
      sessionStorage.closedLastTab = '1';
});
danronmoon
  • 3,814
  • 5
  • 34
  • 56
M Rostami
  • 4,035
  • 1
  • 35
  • 39
  • 2
    This code helps a lot, but the data storage persist when you duplicate the window – Kemen Paulos Plaza Jul 27 '16 at 07:53
  • 1
    The session storage not persist in IE. – Brian Cannard Feb 17 '17 at 21:34
  • 5
    Picking numbers randomly doesn't ensure they are unique (ie pick a random number between 0 and 99, and it is possible to always 11 multiple times). Why not a serial number in localStorage? – Max Waterman Jun 30 '17 at 08:26
  • 1
    @MaxWaterman, yes, random numbers can collide, but `Math.random` returns double precision number, which has size of 64-bit. So it is not that bad. The probability of two duplicate numbers generated by `Math.random` over a **million** runs is about 1%. See this answer https://stackoverflow.com/a/28220928/3167374. And quality of `Math.random` seems to be better in new versions of browsers. Now f.ex. it is 128-bit generator internally used in Chrome (https://v8.dev/blog/math-random). (Not sure how much does it affect probability of collisions through.) – shitpoet Jul 09 '21 at 23:13
  • @shitpoet ok - so low risk - but why do that when you can ensure uniqueness via a serial number? That's not to mention that generating a random number is more costly than just incrementing a serial number. I simply don't see the point in using a random number in this use-case. – Max Waterman Jul 11 '21 at 02:49
  • 4
    for those who are worried about duplicate ids - just use a time based prefix for the random generate id. – Shlomi Hassid Jul 17 '21 at 10:36
  • I dont understand why you cant generate id when the tab loads? – Martin Kosicky Mar 02 '23 at 11:24
15

You have to be using html5, but sessionStorage combined with a random guid would seem to be what you want.

jmoreno
  • 12,752
  • 4
  • 60
  • 91
  • 1
    Numbers can occur twice, even if they are picked randomly, no? So there is a chance that two tabs can have the same number. I would have thought a serial number in localStorage would work better - when you open the page in a new tab, increment the number and store that in sessionStorage. – Max Waterman Jun 30 '17 at 08:22
  • 9
    @MaxWaterman: different browsers, private tabs, changing time...serial is no more guaranteed than random. There are various ways to create a guid that will as good as it gets -- chance of collision so small as to not be worth bothering aboit. – jmoreno Jun 30 '17 at 21:03
  • @MaxWaterman About from 64 bit long numbers, you can safely forget a collision. Not even crypto hashes use ids longer than 128 bits on this reason (to prevent bugs caused by random number collision). – peterh Jun 21 '23 at 06:40
12

Here's a way to have the tabId available after the page was loaded - having the same tabId even after refresh.

This also solves the issue of duplicate tabs, as the tabId is cleared from the 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;
});

Access the tabId using window.tabId.

The beforeunload event is needed in order to persist the tabId to have the same id available after refresh.

The load event is needed to:

  1. Generate the initial tabId.
  2. Load the same tabId after refresh - if exists.

* I don't really know why I should do return null or return at all. Just read that not returning may cause the function to not work :(

AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277
  • Is waiting for load event necessary? Why not set the tabId synchronously when script is executing? Are there any browsers which do not execute scripts in duplicated tabs but still trigger the load event? – Marcin Majkowski Dec 29 '21 at 12:24
  • Use of beforeunload has other, negative, consequences - see MDN. – Julian Knight Dec 22 '22 at 11:34
9

Since I don't have find no simple Javascript function as windows.currentTabIndex, I have written some lines of Javascript to fix an ID on each browser tabs when they are loaded.

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

This code save last tab's index in localStorage to update current value for new tabs and in sessionStorage to fix page's id !

I must call defineTabId() function in each pages of my application. Since I develop using a JSF templates, this is only defined in only one place :-)

schlebe
  • 3,387
  • 5
  • 37
  • 50
  • But what if this code run in two parallel opening-at-same-time tabs? It seems to me there can be a race condition near `getItem` - `setItem` calls. – shitpoet Jul 09 '21 at 23:20
  • If 2 distincts code are executing this code in same time, the program bug. BUT in my situation, it is IMPOSSIBLE to generate multiple tabs in 2 parallel code because creating new tab is linked to human action and it is only possible to open one tab at one time. – schlebe Jul 10 '21 at 06:06
  • If for some reason, it was possible to open mutliple tabs, my function must be call differently. Example, this function must be call previously in tab that create other tab so that multiple tabid are created sequentially in same thread ! – schlebe Jul 10 '21 at 06:09
6

You can not get tab id with javascript, how ever there is some solution that can help you with like using different session / cookies when user open new tab.

Some reference:

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?

Community
  • 1
  • 1
James
  • 13,571
  • 6
  • 61
  • 83
3

For anyone on here using React, I made a sneaky little hook:

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
}

Put this on your base App/Page component, or somewhere that you know will always be mounted.

It'll run the effect after mounting and set a unique id for the current tab in session storage, which is storage specific to the tab.

Duplicating the tab will get you a new id for the new tab, since the component will mount again and the effect will run, overwriting the previous tab's id

Matt Wills
  • 676
  • 6
  • 11
  • 1
    So, what's the point of putting the id in `sessionStorage`, if you're never going to read from it? Also, `uniqid`'s documentation suggests it falls back to only using time in the browser, so using a simple `Date.now()` instead would do the trick I suppose. – thesdev Jul 15 '21 at 04:11
2

One of the possible solutions is using "window.name" property, but I do not want to use it because somebody else can use it.

I found one more possible solution: using sessionStorage. It supported by FF3.5+, Chrome4+, Safari4+, Opera10.5+, and IE8+.

Dennis Liger
  • 1,488
  • 2
  • 13
  • 28
2

You can get the tab ID (or clientId) in "fetch" event handler of your service worker. If you need to send that clientId to your page, just use client.postMessage().

In your service worker:

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

 }());
});

In your page script:

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

Sources: Client.postMessage()

Mike
  • 33
  • 7
  • It's my understanding that the Client `id` is not retained between tab refreshes and a new one is generated, is that correct? I based this assessment on my experience [testing with this demo](https://serviceworke.rs/message-relay/) on macOS Chrome 91.0.4472.114. Note: the demo used a `postMessage()` to the SW to identify the client rather than the Fetch API, but I presume there'd be no difference. – evan.bovie Jul 23 '21 at 21:07
1

If there isn't any odds of a user opening 3 tabs within 2 milliseconds, could use a timestamp and store that into window.name and use that as the key in your lookup. The window.name value will stay with the tab until it is closed.

Timestamp

eaglei22
  • 2,589
  • 1
  • 38
  • 53
0

another variation on a theme: place this in the head section of your html

    <head><script>
           var tabId=sessionStorage.getItem("tabId");
           if (!tabId||(tabId!==sessionStorage.getItem("tabClosed"))){
               tabId=Math.random().toString(36).substr(2)+Date.now().toString(36).substr(5);
               sessionStorage.setItem("tabId",tabId);
           }
           sessionStorage.removeItem("tabClosed");
           window.addEventListener("unload",function(){
               sessionStorage.setItem("tabClosed",tabId);
           });
           document.title=tabId;
    </script></head>

How it works:

First, by placing it in the head, it resolves the tabId immediately, so any loading scripts have access to the tabId, in case that's important to you.

when the page loads, reading from sessionStorage on a new tab will mean tabId is null, in which case a new id is invented.

whenever you close the tab, the tabClosed value is written, and it's immediately removed when the tab is reloaded.

when you duplicate the tab, the session info gets copied, and thus, the tabClosed value won't be there, so a new id is invented then also.

unsynchronized
  • 4,828
  • 2
  • 31
  • 43
0

Seeing all the more complex answers using sessionStorage

.. why not just use the current time?

  var timeId = new Date().getTime();

From OP:

"It could be a random or sequenced number, or a date-time stamp, as long as it remains the same for the life of the tab."

Gene Bo
  • 11,284
  • 8
  • 90
  • 137
-1

At least for chrome, there's an official answer since 2013: chrome.tabs API.

Ofek Shilon
  • 14,734
  • 5
  • 67
  • 101