@stormid/toggle
Version:
Accessible DOM state toggling
183 lines (146 loc) • 5.63 kB
Markdown
# Toggle
Accessible DOM state toggling utility to support the expansion and collapse of regions of HTML documents using aria-expanded. Useful for expandable sections and off-canvas navigation patterns.
For well-tested implementations of UI patterns using Toggle look at https://storm-ui-patterns.netlify.app.
---
## Usage
For well-tested implementations of UI patterns using Toggle look at https://storm-ui-patterns.netlify.app.
To install
```
npm i -S /toggle
```
### Full document toggle
Useful for document-level state changes that affect the whole page, such as an off-canvas menu.
1. Set up the DOM elements
The element you want to toggle state, and related button(s) that will trigger state change. The `data-toggle` attribute of the target element (nav in the example below) is used as a selector to find buttons that trigger state change. The target element should have a unique id.
Simplified example:
```
<button class="js-toggle-btn">Menu</button>
<nav id="primary-navigation" aria-label="Main navigation" class="js-toggle" data-toggle="js-toggle-btn">...</nav>
```
1. Set up CSS
Toggle changes DOM attributes and CSS classNames but all visible changes to the UI are left to the developer to implement in CSS.
For a full document Toggle a className is added to the document element (html) based on the target id - "on--" plus the target id.
Simplified example:
```
.nav {
display: none;
}
.on--primary-navigation .nav {
display: block;
}
```
3. Set up JavaScript
Install
```
npm i -S /toggle
```
Implement
```
import toggle from '@stormid/toggle';
const [ instance ] = toggle('.js-toggle');
```
In addition to a CSS selector, Toggle also supports initialisation via
DOM element
```
const element = document.querySelector('.js-toggle');
const [ instance ] = toggle(element);
```
Node list
```
const elements = document.querySelectorAll('.js-toggle');
const [ instance ] = toggle(elements);
```
Array of elements
```
const elements = [].slice.call(document.querySelectorAll('.js-toggle'));
const [ instance ] = toggle(elements);
```
### Localised toggle
Useful for localised state changes affecting a smaller part of the document, such as an exapandable section.
1. Set up the DOM
Simplified example
```
<div class="expandable">
<button type="button" class="js-toggle__btn"></button>
<div id="child" class="js-toggle__local child" data-toggle="js-toggle__btn"></div>
</div>
```
2. Set up CSS
A className ('is--active') is added to the parentNode of the target in a localised toggle.
Simplified example
```
.child {
display: none
}
.parent.is--active .child {
display: block;
}
```
## Options
```
{
delay: 0, //duration of animating out of toggled state
startOpen: false, //initial toggle state
local: false, // encapsulate in small part of document
prehook: false, //function to fire before each toggle
callback: false, //function to fire after each toggle
focus: true, //focus on first focusable child node of the target element
trapTab: false, //trap tab in the target element
closeOnBlur: false, //close the target node on losing focus from the target node and any of the toggles
closeOnClick: false, //close the target element when a non-child element is clicked
useHidden: false //add and remove hidden attribute to toggle target
}
```
e.g.
```
const [ instance ] = toggle('.js-toggle', {
startOpen: true
});
```
Options can also be set on an instance by adding data-attributes to the toggle element, e.g.
```
<div class="parent">
<button type="button" class="js-toggle__btn"></button>
<div class="js-toggle__local" data-toggle="js-toggle__btn" data-start-open="true"></div>
</div>
```
A toggle can also be started open usng the active className alone, e.g.
```
<div class="parent is--active">
<button type="button" class="js-toggle__btn"></button>
<div class="js-toggle__local" data-toggle="js-toggle__btn"></div>
</div>
```
### Developer note 06 Sep 2022: Use of closeOnBlur
It should be noted that at the time of writing, the availaibility of the blur event was limited on mobile assistive tech, specifically iOS VoiceOver.
When a user is swiping through content in VoiceOver, the focus/blur events will only fire if the focus is moving to or from a form input element or button. The focus/blur events will not fire when moving between links, headings or in-page content. Any use of the closeOnBlur setting should be carefully tested to make sure that the behaviour is as expected on these devices.
## API
Inititalisation returns an array of instances for each target element. Each instance exposes the interface
```
{
node, DOMElement, the element to expand and collapse
startToggle, a Function that starts the toggle lifecycle with prehook, toggle, and post-toggle callback
toggle, a Function that just executes the toggle
getState, a Function that returns the current state Object
}
```
## Events
There are two custom events that an instance of the toggle dispatches:
- `toggle.open` when it opens
- `toggle.close` when closes
The events are dispatched on the same element used to initialise the toggle and bubble for event delegation. A reference to the getState function of the instance is contained in the custom event detail.
```
const [ instance ] = toggle('.js-toggle');
//event bubbles so can delegate
//could also add event listener to document.querySelector('.js-toggle')
document.addEventListener('toggle.open', e => {
const { node, toggles } = e.detail.getState();
// do something
});
```
## Tests
```
npm t
```
## License
MIT