Accessible, dependency-free disclosure implementation
https://disclosure-chalkygames123.netlify.app/
Copy src/modules/disclosure.js to your project.
Here is the basic markup, which can be enhanced. Pay extra attention to the comments.
<!-- 1. The summary element -->
<button type="button" aria-controls="details">Summary</button>
<!-- 2. The details element -->
<div id="details" aria-hidden="true">Details</div>
-
The summary element.
- It has to have the
aria-controls
attribute, which matches theid
of the details element.
- It has to have the
-
The details element.
- It has to have the
id
attribute. - It should have an initial
aria-hidden="true"
attribute to avoid a "flash of unhidden content" on page load.
- It has to have the
The script itself does not take care of any styling whatsoever, not even the display
property. Instead, it toggles the aria-expanded
attribute on the summary element and the aria-hidden
attribute on the details element.
Here is a solid set of styles to get started (note that you might have to rename the class names to fit your code):
.details {
overflow: hidden;
transition: height 0.5s;
contain: content;
}
.details[aria-hidden='true'] {
height: 0;
visibility: hidden;
transition: height 0.5s, visibility 0s 0.5s;
}
The rest, such as what the disclosure really looks like, and how its content is styled, is left at your discretion. These styles should be enough to get you on the right track. Feel free to look at the demo for more extensive styles.
Pass the summary element to the constructor.
// Get the summary element (with the accessor method you want)
const summaryElement = document.querySelector('#your-summary-id');
// Instantiate a new Disclosure module
const disclosure = new Disclosure(summaryElement);
The constructor takes an options object as a second argument. Currently, only one option is available:
Name | Type | Default | Description |
---|---|---|---|
hashNavigation | boolean | false | Whether the disclosure is automatically opened if the ID of the summary element matches the URL fragment on initialization |
The DOM API relies on data-*
attributes. They all live under the data-disclosure-*
namespace for consistency, clarity, and robustness. Currently, only one attribute is recognized:
data-disclosure-close
: the closest parent disclosure will be the target
The following button will close the disclosure in which it lives when interacted with.
<button type="button" data-disclosure-close>Close</button>
Regarding the JS API, it simply consists on open()
, close()
and toggle()
methods on the disclosure instance.
// Open the disclosure
disclosure.open();
// Close the disclosure
disclosure.close();
// Toggle the disclosure
disclosure.toggle();
When interaction, the summary element will emit certain events. It is possible to subscribe to these with the addEventListener()
method.
disclosure.addEventListener('open', function (event) {
// Do something when disclosure gets open
});
disclosure.addEventListener('opened', function (event) {
// Do something when disclosure gets opened
});
disclosure.addEventListener('close', function (event) {
// Do something when disclosure gets close
});
disclosure.addEventListener('closed', function (event) {
// Do something when disclosure gets closed
});