[HN Gopher] Show HN: Offline JavaScript PubSub between browser tabs
___________________________________________________________________
Show HN: Offline JavaScript PubSub between browser tabs
Author : l1am0
Score : 72 points
Date : 2025-04-03 14:09 UTC (8 hours ago)
(HTM) web link (simon-frey.com)
(TXT) w3m dump (simon-frey.com)
| edweis wrote:
| I love it, the code is so simple:
| https://github.com/simonfrey/tabsub/blob/master/tabsub.v1.js
| l1am0 wrote:
| Thank you :) Simple because I don't know any better :'D
| stanac wrote:
| I did something similar (local storage events) in time of
| jQuery to sync shopping cart between tabs. Very simple
| solution, but, IIRC, it didn't work with IE.
| sisk wrote:
| If I understand this correctly, the `BroadcastChannel` API solves
| a similar purpose.
|
| https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_C...
| l1am0 wrote:
| Oh, did not know about it :D Thank you! Added a link to this to
| the TabSub landingpage
| sisk wrote:
| My pleasure--happy to share an alternative. Thanks for making
| and sharing a thing!
| EGreg wrote:
| I remember implementing the same thing in our framework like a
| decade ago, but eventually taking it out. We wanted to use it for
| caching data between tabs, so as not to hit the server again per
| tab:
|
| https://github.com/Qbix/Platform/blob/main/platform/plugins/...
|
| It had the concept of a "main frame" that they would route all
| requests to the server through.
|
| I remember now -- I did it not so much for tabs as for our
| solution of having tons of little iframes on a page:
| https://qbix.com/ecosystem
| remram wrote:
| You can use a SharedWorker now instead of picking a tab to do
| that work (and failing over to another tab if it gets closed).
| Support is still spotty though.
| EGreg wrote:
| Well, you can just use a service worker, instead of broadcast
| channels or shared workers. They have all the functionality
| you need, now.
|
| Any iframe that loads in a secure context and itself creates
| a secure context, can load a service worker for its domain or
| subdomain or path.
| kvemkon wrote:
| Related recently discussed:
|
| Show HN: JavaScript PubSub in 163 Bytes (31.03.2025)
|
| https://news.ycombinator.com/item?id=43529774
| Lerc wrote:
| I built a ComfyUi node that let you run a little paint program in
| another tab. (Sorry about the lack of updates if you use it. I
| intend to get back to it)
|
| Negotiating the communication between tabs was by far the hardest
| part. In the end I ended up using local storage for signaling to
| establish a dedicated messsageport channel.
|
| It was such a fight to make something that re-established the
| connection when either page reloaded.
|
| There are still some connection issues that I haven't been able
| to resolve. It seems some browsers on some systems reject
| messages between tabs if they were loaded hours apart.
|
| It might be worth benchmarking a pure local storage fallback, but
| I'm guessing it would suffer with high traffic.
|
| A generalised implementation that allowed switching multiple
| paint programs and multiple ComfyUi pages would be ideal. A
| PubSub might be the way to go.
|
| There's also the issue of other parts of the app also using local
| storage. Need to not step on toes.
| hombre_fatal wrote:
| It's nice that the 'storage' event also gives you event.newValue
| to spare you the race condition of reading from localStorage.
| qudat wrote:
| Very cool, I love seeing pubsubs in the wild. We built an ssh
| based pubsub that we have been using in production for awhile
| now: https://pipe.pico.sh/
| Minor49er wrote:
| The audio demo makes me think of Bandcamp which will pause music
| that you're playing if another Bandcamp tab starts playing a song
| separately. Must be a similar mechanism under the hood
| realPubkey wrote:
| This is likely done with the WebLocks API
| sltkr wrote:
| That doesn't allow signaling another tab, does it?
| 0x457 wrote:
| > The Web Locks API allows scripts running in one tab or
| worker to asynchronously acquire a lock, hold it while work
| is performed, then release it. While held, no other script
| executing in the same origin can acquire the same lock,
| *which allows a web app running in multiple tabs or workers
| to coordinate work and the use of resources.*
|
| source: https://developer.mozilla.org/en-
| US/docs/Web/API/Web_Locks_A...
| sltkr wrote:
| I read that, but how can that API be used to notify the
| original tab to stop playing?
| nottorp wrote:
| Isn't this a security hole waiting to be exploited?
|
| How does the browser handle access control to the local storage,
| especially offline when they aren't loaded from the same site?
|
| [Yes, I really don't know. Yes, I'm asking. Not everyone is a web
| dev.]
| sltkr wrote:
| From the post:
|
| > As TabSub uses local store this only works on the same
| domain, as the browser separates the local storage by domains
| as security measure.
|
| (More precisely, the separation is based on _origin_ , which is
| roughly the combination of protocol, hostname, and port.)
|
| The conclusion is this only works between tabs that have the
| same website open.
| nottorp wrote:
| But it's offline, what's the website? Or offline doesn't mean
| offline?
| godot wrote:
| I think in their case, offline is as in you don't need to
| set up a pubsub server and the client doesn't have to talk
| to a server for the specific pubsub functionality, not as
| in "use this for offline web pages/html files locally" (it
| may or may not work for that, I have no idea, didn't look).
| l1am0 wrote:
| Works for offline apps as well :) (you would need to
| download the tabsub JS ofc and not use it from my server)
| l1am0 wrote:
| It means, that you don't need an internet connection for
| this to work :) (so it is no rabbitmq or so which runs on a
| server and the browser is just the client)
|
| You can try on the demopage when you
|
| 1. play the songs each (for them to buffer a little audio
| snippet)
|
| 2. open the page in a second tab
|
| 3. Disconnect from the internt
|
| Still works :D
| nottorp wrote:
| I'm just asking random questions now and then to get an
| idea of how things work in the web world. Especially
| those that aren't mentioned in tutorials.
| sltkr wrote:
| The subscribe() implementation seems suboptimal in the case where
| there are many different topics:
| subscribe(topic, callback) { if
| (this.listeners[topic] == undefined) { // Not yet
| any listener for this topic this.listeners[topic]
| = []; window.addEventListener('storage', (e)
| => { const dataKey = topic + "_data";
| if (e.key === dataKey) { const data =
| JSON.parse(e.newValue)[0];
| this.listeners[topic].forEach((v, k) => {
| v(data); }); }
| }, false); }
| this.listeners[topic].push(callback) }
|
| This installs a handler for every single topic, and every time a
| message is published, the handlers for all topics are called,
| even though at most one is interested in the change. A more
| efficient implementation would install a single handler, e.g.
| (untested): window.addEventListener('storage',
| (e) => { if (e.key.endsWith('_data')) {
| const topic = e.key.substring(0, e.key.length - 5);
| const data = JSON.parse(e.newValue)[0];
| this.listeners[topic]?.forEach(v => v(data)); }
| }, false);
| l1am0 wrote:
| Will check this tomorrow. If you come to it in the meantime,
| feel free to open a PR :) Thx
| amazingamazing wrote:
| I wonder how one does this on nodejs across instances gracefully.
| kolme wrote:
| Well, there's RabbitMQ and the rest of the message queues out
| there. But I don't know if those fall in your definition of
| "graceful".
| amazingamazing wrote:
| No, I mean with just NodeJS only.
___________________________________________________________________
(page generated 2025-04-03 23:01 UTC)