Cross-Tab Title Hints
Let's say you've built a website that shows items—maybe things you can buy online, or a catalogue of TV episodes. Your users are opening lots of tabs at once 📈 to compare and contrast, or decide what to buy, or whatever.
Turns out, we can use just the hidden tabs as a surface to help the user out. But note that this is really a desktop-only feature, because tabs aren't really visible on mobile. 📱
So first! Play with the demo:
And check out the video if you'd like to hear about it in <2 minutes:
The very short version of this is: if your users are trying to compare and contrast items, you can give your users visual cues by taking over hidden tabs' titles (and favicons, but that's an exercise for the reader) and showing them relevant information–without having to switch tabs.
To put it differently:
Build It
Let's talk about what makes this work and how to build it!
Broadcast Channel
First, let's learn about the humble BroadcastChannel
.
This is a tool which lets all pages and workers on the same domain broadcast messages to another.
You can actually test it entirely on one page, although that's not really powerful on its own:
const b1 = new BroadcastChannel('some-topic-name');
const b2 = new BroadcastChannel('some-topic-name');
b2.onmessage = ({ data }) => console.info('got data', data);
// will go to b2 and other matching channels
b1.postMessage('hi');
The power 🔌 comes in that any other page that creates the same topic'ed BroadcastChannel
will recieve the message too.
Neat!
This is pretty much as basic as it gets.
It's not enormously different than posting directly to a Worker
or another window, except that it doesn't support transferable objects—there's not a 1:1 mapping to the target, so it's not clear who would own it. 📝
Focus Management
This blog post is a bit of a 2-for-1: focus management is wholly unrelated to 🔊 broadcast, but here we are.
The problem: you want to do something while an element is focused, and stop when it's not.
I think naïvely people think, okay, let's create one "focus"
and one "blur"
handler and try to maintain state.
But you can honestly get yourself into knots. 🪢
It's simpler to instead, set up one handler which itself adds a cleanup task:
thing.addEventListener('focus', () => {
doStart();
thing.addEventListener('blur', () => {
doStop();
}, { once: true }); // important!
});
This works because the browser basically guarantees event order—we'll always see an element focus and blur before any others do. This also works when a tab is wholly unfocused—your browser only has one tab and one element active at once 1️⃣, not one tab per page. ❌📃📃📃
(In the video I talk about "focusout"
, which just bubbles up—I feel like there's some reason it's safer to use, but "blur"
is probably fine here too.)
Putting It Together
Now that we can track focus, and broadcast, we can request that other tabs make noise. Start with HTML like:
<table>
<tr tabindex="0" id="tableRow">
<th>Rating</th>
<td>⭐⭐⭐</td>
</tr>
</table>
And JS to match:
const channel = new BroadcastChannel('displayInfo');
tableRow.addEventListener('focus', () => {
channel.postMessage({ action: 'start', prop: 'rating' });
tableRow.addEventListener('blur', () => {
channel.postMessage({ action: 'stop' });
}, { once: true });
});
Great! …except, we now need to listen to requests and update our title accordingly:
const originalTitle = document.title;
channel.onmessage = ({ data }) => {
if (data.action === 'start') {
document.title = getMyRating() + '-' + originalTitle;
} else {
document.title = originalTitle;
}
};
function getMyRating() {
// TODO: something like:
return tableRow.querySelector('td').textContent;
}
And that's it, albeit a fairly simplified version. Easy visibility for your users to compare and contrast information. 📊
Done
Thanks for reading!
There are some caveats to this demo. First and foremost, as I mentioned above, this doesn't really help you on mobile, because tab titles are rarely (if at all) visible.
Secondly, and this is a big one, browsers may totally reserve the right to stop you being annoying.
And this feature does drift dangerously close to that: imagine a tab that, when opened, constantly changed its title to say "HEY LOOK AT ME".
My hunch is that if you're above a certain engagement threshold—and this is a concept inside Chrome and others, which you can find at chrome://site-engagement/
—it'll be allowed, and not otherwise.
That's probably fine.
This was largely inspired by a tweet I can't find any more, where someone claimed that IKEA was doing this for furniture sizes. Actually, this was a cute feature of Safari—it was getting rid of a common title prefix. But I think it's cute to do yourself, and the interactivity lets us extend on it just a little bit. 🆒
Follow me on Twitter for more like this. 🐦