A11y Toggle
Get the code on GitHub or skip to a specific documentation section right away:
Getting started
To get started, simply include a11y-toggle.js (ideally the minified version, of course) in your document. It can be either in the <head>
or at the bottom of the <body>
although the latter the better, for performance reasons.
<script src="a11y-toggle.min.js" async></script>
That’s it! There is no need to initialize anything as everything else is being handled by the script itself. Simply check the next section to see how to declare your toggles.
Basic example
The bare minimum is a button with the data-a11y-toggle
attribute mapped to an existing id
in the document. You can have a look at the code below.
<button type="button" data-a11y-toggle="target">
Toggle code »
</button>
<div id="target">
Some content…
</div>
Initial ARIA-specific attributes such as aria-expanded
, aria-hidden
and aria-labelledby
, as well as id
on the toggle element (if none already) are being added automatically.
On the CSS Side, only hiding aria-hidden
content is strictly necessary. It is also recommended to hide the button when JavaScript is disabled, by checking the presence of the aria-controls
attribute for instance.
[aria-hidden='true'],
[data-a11y-toggle]:not([aria-controls]) {
display: none;
}
Note: globally hiding elements with [aria-hidden="true"]
can have rendering issues with third party integrations. Only known to date is with Google Maps, in which it prevents the map tiles to render. Therefore it needs to be resetted inside a Google Maps container.
Expanded by default
If you want the content to be expanded by default, you can add a data-a11y-toggle-open
attribute to the target element (not the toggle itself).
Note that this is the default behaviour when JavaScript is disabled, so that the content remains accessible even without JavaScript.
<button type="button" data-a11y-toggle="target">
Toggle code »
</button>
<div id="target" data-a11y-toggle-open>
Some content that is expanded by default…
</div>
Multi toggles
A collapsible container can have several toggles able to control its visibility. There is no difference in markup regarding multi toggles.
<button type="button" data-a11y-toggle="target">
Toggle code »
</button>
<button type="button" data-a11y-toggle="target">
Toggle code »
</button>
<div id="target">
Some content…
</div>
Animations
Given that a11y-toggle is completely unopinionated regarding the styling layer, it is really up to the developer to implete the sliding and/or fading animation. Here is an example below. Hat tip to Nicolas Hoffman.
The markup does not change compared to the original version except that we add a class to the collapsible sections to be able to target them in CSS.
<button type="button" data-a11y-toggle="target">
Toggle code »
</button>
<div class="collapsible-box" id="target">
Some content…
</div>
The styles do not rely on display: none
anymore but a tricky / hacky combination of declarations to make the magic happen.
.collapsible-box {
overflow: hidden;
opacity: 1;
max-height: 80em;
visibility: visible;
transition:
visibility 0s ease,
max-height 2s ease,
opacity 2s ease;
transition-delay: 0s;
}
.collapsible-box[aria-hidden='true'] {
max-height: 0;
opacity: 0;
visibility: hidden;
transition-delay: 2s, 0s, 0s;
}
Connected toggles
The library does not provide out-of-the-box support for connected toggles, which are a set of toggles with only one expanded at any time.
However with a tiny bit of JavaScript, it is possible to leverage the capabilities of the library to add this feature.
<div class="connected-toggles">
<button data-a11y-toggle="target-1" type="button">
Toggle content 1 »
</button>
<button data-a11y-toggle="target-2" type="button">
Toggle content 2 »
</button>
<div id="target-1">
Some content (1)…
</div>
<div id="target-2">
Some content (2)…
</div>
</div>
The trick is to collapse all targets in the set when one is being activated, except this one. The JavaScript snippet to achieve that should be pretty straight-forward.
function collapse (toggle) {
var id = toggle.getAttribute('data-a11y-toggle');
var collapsibleBox = document.getElementById(id);
collapsibleBox.setAttribute('aria-hidden', true);
toggle.setAttribute('aria-expanded', false);
}
function collapseAll (event) {
toggles
.filter(function (toggle) {
return toggle !== event.target;
})
.forEach(collapse);
}
var toggles = Array.prototype.slice.call(
document.querySelectorAll('.connected-toggles [data-a11y-toggle]')
);
toggles.forEach(function (toggle) {
toggle.addEventListener('click', collapseAll);
});
Note that if you have several instances of connected toggles on your page, you might want to read this issue, as the current code is too simple to cover this edge case.
Beware! a11y-toggle is not a library to build tabs. While it certainly is possible to do so, remember that tabs imply other accessibility concerns which should be taken care of.
Dynamically injected toggles
a11y-toggle fires once when the DOM is fully loaded (DOMContentLoaded
). You will have to relaunch it if you dynamically add new toggles with JavaScript.
Thankfully, a11y-toggle exposes an a11yToggle
function on the global object.
window.a11yToggle();
If you do not want to reinitialise everything, you can pass a context to the a11yToggle
function which will be used as a root for .querySelectorAll
.
var newContainer = document.getElementById('new-container');
window.a11yToggle(newContainer);