Announcing document's title changes with aria-live
Important: The content that follows is an experiment with very limited testing. It would need testing on a broader range of browser and screenreader combination before being used in production.
Building a single page applications requires taking over the browser's responsibility for navigating from page to page fr. This means responding to changes to the page URL and showing the page that corresponds, of course. But browsers also broadcast the title of the new page to assistive technologies, making the new location clear to the users. This is also something that needs to be taken care of.
The Accessible Rich Internet Application (ARIA) specification brings the aria-live
attribute to let assistive technologies announce updates of the page as they happen. It has good support, so could it be used on the document's title tag? This is a test to find out.
The experiment
This page hosts an experiment to test if the title gets announced OK upon update. Click a button, the title gets updated and hopefully announced.
To witness the announcements, you'll need to have a screen-reader alongside your browser. Both Mac, Windows 10 and Ubuntu come with one built in, and NVDA can be freely installed on Windows.
Before trying to update a fancy element like <title>
, let's start by checking everything works as intended with a more common candidate for aria-live
: a simple <p>
element,for example.
Clicking the next button will update a <p>
tag right after. Because it has an aria-live="polite"
attribute, this should trigger an annoucement of "Clicks" followed by a number.
Clicks: 0
Now for the actual test, the form that follows will trigger an update of the document's title. If things work, it should trigger the same kind of announcement: "Clicks" followed by a number. The title in the tab will be updated too, but that part is more likely to work.
It offers two ways to update the title, in case it makes a difference (both worked in my limited testing):
- updating the
document.title
property directly - fishing the title element from the
<head>
and updating itstextContent
Behind the scene
The first step is to add the aria-live
attribute to the <title>
element. Not sure if having it in the markup when the page loads would trigger a duplicate announcement (with the title already announced by the browser), so I prefered playing is safe and add it via JavaScript.
var titleElement = document
.head
.querySelector("title");
// Announcement feels important, so using `assertive`
titleElement.setAttribute('aria-live', 'assertive');
// Same for the `<p>`, except it's not as important so `polite`
document.getElementById('live-div').setAttribute('aria-live','polite')
Only adding the attribute is not enough, though. The <title>
element hidden by default and doesn't make its way into the accessibility tree.
Using display:block
on the <head>
and itself will allow it to make its way there.
As we don't want it shown on the page, though, we need to use some CSS to hide it accessibly:
head {
display: block;
}
title {
display: block;
/*
From Bootstrap's visually-hidden mixin
https://github.com/twbs/bootstrap/blob/main/scss/mixins/_screen-reader.scss#L8
*/
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border: 0 !important;
}
With that, any change to the title should get announced. If not all changes needed announcement, toggling either the display
of the <title>
or the aria-live
attribute as necessary should give control. But it would need further testing, past this experiment focusing only on aria-live
being recognised.
For the updates, it's a little more JavaScript to listen to events of the form and button, and update the appropriate values:
var value = 0;
var content = "Clicks: " + value;
function updateContent() {
value++;
content = "Clicks: " + value;
}
document.addEventListener("click", function(event) {
// Only consider clicks on buttons
if (event.target.tagName == "BUTTON") {
// Grab the target on the page
var updateTarget = event.target.getAttribute("data-target");
if (updateTarget) {
updateContent();
document.getElementById(updateTarget).innerHTML = content;
}
}
});
document.addEventListener('submit', function(event) {
// Otherwise, the page reloads, whoops!
event.preventDefault();
updateContent();
// Grab which way we should update the title
if (document.querySelector('[name="updateType"]:checked').value === 'property') {
document.title = content;
} else {
document.head.querySelector('title').textContent = content;
}
});
Now hopefully that works across all kinds of browsers and screenreaders, but I could only test with Firefox + Orca on Ubuntu and Safari + Voiceover on Mac. If you try it with another combination, I'd be glad to know.
OK, for real, next article will be back to the static site generator.
Updates
23 Jul 2020: Add a link detailing what needs to be handled when taking over the browser navigation with JavaScript. Thanks Yann fr for the link, and Audrey fr for the article!