a11y-dialog is a lightweight (1.2Kb) yet flexible script to create accessible dialog windows. It has no dependency, a JavaScript API, a DOM API and event handling.
Introduction
This repository is a fork from accessible-modal-dialog ↗ by Greg Kraus. We at Edenspiekermann are big fans of the original version, although we discovered we could improve it and make it even better. On top of that, the original script depends on jQuery, which happened to be a problem for us.
The original repository being apparently unmaintained, we decided to fork it and release our own version of the accessible modal dialog. All credits to the original author.
You can try the live demo ↗.
Installation
npm install a11y-dialog --save
bower install espi-a11y-dialog
Or you could also copy/paste the script in your project directly, but you will be disconnected from this repository, making it hard for your to get updates.
Usage
You will find a concrete demo in the example folder of this repository, but basically here is the gist:
Expected DOM structure
Here is the basic markup, which can be enhanced. Pay extra attention to the comments.
<div id="main">
</div>
<div id="my-accessible-dialog" aria-hidden="true">
<div tabindex="-1" data-a11y-dialog-hide></div>
<div role="dialog" aria-labelledby="dialog-title">
<div role="document">
<h1 id="dialog-title" tabindex="0">Dialog Title</h1>
<button type="button" data-a11y-dialog-hide aria-label="Close this dialog window">
×
</button>
</div>
</div>
</div>
Styling layer
You will have to implement some styles for the dialog to “work” (visually speaking). The script itself does not take care of any styling whatsoever, not even the display
property. It basically mostly toggles the aria-hidden
attribute on the dialog itself and its counterpart containers. You can use this to show and hide the dialog:
.dialog[aria-hidden='true'] {
display: none;
}
JavaScript instantiation
const el = document.getElementById('my-accessible-dialog');
const dialog = new A11yDialog(el);
As recommended in the HTML section of this documentation, the dialog element is supposed to be on the same level as your content container(s). Therefore, the script will toggle the aria-hidden
attribute of the siblings of the dialog element as a default. You can change this behaviour by passing a NodeList
, an Element
or a selector as second argument to the A11yDialog
constructor:
const dialog = new A11yDialog(el, containers);
DOM API
The DOM API relies on data-*
attributes. They all live under the data-a11y-dialog-*
namespace for consistency, clarity and robustness. Two attributes are recognised:
data-a11y-dialog-show
: the id
of the dialog element is expected as a valuedata-a11y-dialog-hide
: the id
of the dialog element is expected as a value; if omitted, the closest parent dialog element (if any) will be the target
The following button will open the dialog with the my-accessible-dialog
id when interacted with.
<button type="button" data-a11y-dialog-show="my-accessible-dialog">Open the dialog</button>
The following button will close the dialog in which it lives when interacted with.
<button type="button" data-a11y-dialog-hide aria-label="Close the dialog">×</button>
The following button will close the dialog with the my-accessible-dialog
id when interacted with. Given that the only focusable elements when the dialog is open are the focusable children of the dialog itself, it seems rather unlikely that you will ever need this but in case you do, well you can.
<button type="button" data-a11y-dialog-hide="my-accessible-dialog" aria-label="Close the dialog">×</button>
JS API
Regarding the JS API, it simply consists on show()
and hide()
methods on the dialog instance.
dialog.show();
dialog.hide();
For advanced usages, there are create()
and destroy()
methods. These are responsible for attaching click event listeners to dialog openers and closers. Note that the create()
method is automatically called on instantiation so there is no need to call it again directly.
dialog.destroy();
dialog.create();
If necessary, the create()
method also accepts the targets
containers (the one toggled along with the dialog element) in the same form as the second argument from the constructor. If omitted, the one given to the constructor (or default) will be used.
Events
When shown, hidden and destroyed, the instance will emit certain events. It is possible to subscribe to these with the on()
method which will receive the dialog DOM element and the trigger DOM element (if any).
dialog.on('show', function (dialogEl, triggerEl) {
});
dialog.on('hide', function (dialogEl, triggerEl) {
});
dialog.on('destroy', function (dialogEl) {
});
dialog.on('create', function (dialogEl) {
});
You can unregister these handlers with the off()
method.
dialog.on('show', doSomething);
dialog.off('show', doSomething);