A WAI-ARIA friendly accordion component.
Note: The accordion has been refactored and is not compatible with accordion prior to version 0.9.13
An accordion component is a collection of expandable panels associated with a common outer container. Panels consist of a tab header and an associated content region or panel. The primary use of an Accordion is to present multiple sections of content on a single page without scrolling, where all of the sections are peers in the application or object hierarchy. The general look is similar to a tree where each root tree node is an expandable accordion header. The user navigates and makes the contents of each panel visible (or not) by interacting with the Accordion tab header.
- The accordion component relates to the guidelines given in WAI-ARIA Authoring Practices 1.1, Accordion
- User interactions via keyboard or mouse
- Toggle a particular tab using enter or space key, or by clicking a tab
- Client can interact with accordion using a public api og by dispatching a custom action event
- The accordion emits a custom toggle events reflecting the tab toggled
1. Code a <ul>
element with class="mdlext-accordion mdlext-js-accordion mdlext-accordion--horizontal"
to hold the accordion with horizontal layout.
<ul class="mdlext-accordion mdlext-js-accordion mdlext-accordion--horizontal">
</ul>
2. Code a <li>
element with class="mdlext-accordion__panel"
to hold an individual accordion panel.
<ul class="mdlext-accordion mdlext-js-accordion mdlext-accordion--horizontal">
<li class="mdlext-accordion__panel">
</li>
</ul>
3. Code a <header>
element with class="mdlext-accordion__tab"
to hold the accordion tab header.
<ul class="mdlext-accordion mdlext-js-accordion mdlext-accordion--horizontal">
<li class="mdlext-accordion__panel">
<header class="mdlext-accordion__tab">
</header>
</li>
</ul>
4. Code a <span>
element with class="mdlext-accordion__tab__caption"
to hold the accordion tab header caption.
<ul class="mdlext-accordion mdlext-js-accordion mdlext-accordion--horizontal">
<li class="mdlext-accordion__panel">
<header class="mdlext-accordion__tab">
<span class="mdlext-accordion__tab__caption">A tab caption</span>
</header>
</li>
</ul>
5. Code a <section>
element with class="mdlext-accordion__tabpanel"
to hold the tab content.
<ul class="mdlext-accordion mdlext-js-accordion mdlext-accordion--horizontal">
<li class="mdlext-accordion__panel">
<header class="mdlext-accordion__tab">
<span class="mdlext-accordion__tab__caption">A tab caption</span>
</header>
<section class="mdlext-accordion__tabpanel">
<p>Content goes here ...</p>
</section>
</li>
</ul>
6. Repeat steps 2..5 for each accordion panel required.
Multiselectable vertical accordion with three panels, aria attributes, ripple effect on each tab header, decorated with a glyph left and a state icon right. Tab #1 is open at page load (aria-expanded="true"). Subscribes to accordion toggle event.
<ul id="my-accordion"
class="mdlext-accordion mdlext-js-accordion mdlext-accordion--vertical mdlext-js-ripple-effect"
role="tablist" aria-multiselectable="true">
<li class="mdlext-accordion__panel" role="presentation">
<header class="mdlext-accordion__tab" role="tab" aria-expanded="true">
<i class="material-icons">dns</i>
<span class="mdlext-accordion__tab__caption">First section. A long caption should not push the state icon</span>
<i class="mdlext-aria-toggle-material-icons"></i>
</header>
<section class="mdlext-accordion__tabpanel" role="tabpanel" aria-hidden="false">
<h5>Content #1 goes here</h5>
<p>Some content <a href="#">with an anchor</a> as a focusable element.</p>
</section>
</li>
<li class="mdlext-accordion__panel" role="presentation">
<header class="mdlext-accordion__tab" role="tab" aria-expanded="false">
<i class="material-icons">all_inclusive</i>
<span class="mdlext-accordion__tab__caption">Tab #2</span>
<i class="mdlext-aria-toggle-material-icons"></i>
</header>
<section class="mdlext-accordion__tabpanel" role="tabpanel" aria-hidden="true" hidden>
<h5>Content #2 goes here</h5>
<p>Some content....</p>
</section>
</li>
<li class="mdlext-accordion__panel" role="presentation">
<header class="mdlext-accordion__tab" role="tab" aria-expanded="false">
<i class="material-icons">build</i>
<span class="mdlext-accordion__tab__caption">Tab #3</span>
<i class="mdlext-aria-toggle-material-icons"></i>
</header>
<section class="mdlext-accordion__tabpanel" role="tabpanel" aria-hidden="true" hidden>
<h5>Content #3 goes here</h5>
</section>
</li>
</ul>
<script>
'use strict';
window.addEventListener('load', function() {
var accordion = document.querySelector('#my-accordion');
accordion.addEventListener('toggle', function(e) {
console.log('Accordion toggled. State:', e.detail.state, 'Source:', e.detail.tab);
});
});
</script>
Note: All required aria attributes will be added by the accordion component during initialization - so it is not strictly necessary to apply the attributes in markup.
- The snippets/accordion.html and the tests provides more detailed examples.
- Try out the live demo
The accordion interacts with the following keyboard keys.
- Tab - When focus is on an accordion (tab)header, pressing the Tab key moves focus in the following manner:
- If interactive glyphs or menus are present in the accordion header, focus moves to each in order.
- When the corresponding tab panel is expanded (its aria-expanded state is 'true'), then focus moves to the first focusable element in the panel.
- If the panel is collapsed (its aria-expanded state is 'false'), OR, when the last interactive element of a panel is reached, the next Tab key press moves focus as follows:
- Moves focus to the next logical accordion header.
- When focus reaches the last header, focus moves to the first focusable element outside the accordion component.
- Left arrow
- When focus is on the accordion header, a press of up/left arrow keys moves focus to the previous logical accordion header.
- When focus reaches the first header, further up/left arrow key presses optionally wrap to the first header.
- Right arrow
- When focus is on the accordion header, a press of down/right arrow key moves focus to the next logical accordion header.
- When focus reaches the last header, further down/right arrow key presses optionally wrap to the first header
- Up arrow - behaves the same as left arrow
- Down arrow - behaves the same as right arrow
- End - When focus is on the accordion header, an End key press moves focus to the last accordion header.
- Home - When focus is on the accordion header, a Home key press moves focus to the first accordion header.
- Enter or Space - When focus is on an accordion header, pressing Enter ir Space toggles the expansion of the corresponding panel.
- If collapsed, the panel is expanded, and its aria-expanded state is set to 'true'.
- If expanded, the panel is collapsed and its aria-expanded state is set to 'false'.
- Shift+Tab - Generally the reverse of Tab.
Interaction with the component programmatically is performed receiving events from the component or by sending events to the component (or by using the public api).
A client can send a command
custom event to the accordion. The command event holds a detail object defining the action
to perform and a target for the action.
The detail object has the following structure:
detail: {
action, // "open", "close", "toggle" or "upgrade"
target // Target, panel or tab, of action, "undefined" if all panels should be targeted.
// Note: If you send a null target, the action is cancelled
}
Possible actions are:
Open a targeted tab and it's corresponding tabpanel.
myAccrdion = document.querySelector('#my-accordion');
target = myAccordion.querySelector('#my-accordion .mdlext-accordion__panel:nth-child(3)');
ce = new CustomEvent('command', { detail: { action : 'open', target: target } });
If target
is undefined, the action will open all panels.
Note: Opening all panels only makes sense if the accordion has the aria attribute aria-multiselectable
set to true
,
and will be cancelled otherwise.
Close a targeted tab and its corresponding tabpanel.
myAccrdion = document.querySelector('#my-accordion');
target = myAccordion.querySelector('#my-accordion .mdlext-accordion__panel:nth-child(3)');
ce = new CustomEvent('command', { detail: { action : 'close', target: target } });
If target
is undefined, the action will close all panels.
Note: Closing all panels only makes sense if the accordion has the aria attribute aria-multiselectable
set to true
,
and will be cancelled otherwise.
Toggle a targeted tab. Open or close a targeted tab and it's corresponding tabpanel.
myAccrdion = document.querySelector('#my-accordion');
target = myAccordion.querySelector('#my-accordion .mdlext-accordion__panel:nth-child(3)');
ce = new CustomEvent('command', { detail: { action : 'toggle', target: target } });
If target
is undefined, the action will be cancelled.
Upgrade a targeted panel. If you add a panel to the accordion after the page has loaded, you must call upgrade
to
notify the accordion component about the new panel.
myAccrdion = document.querySelector('#my-accordion');
addedPanel = myAccordion.querySelector('#my-accordion .mdlext-accordion__panel:nth-child(4)');
ce = new CustomEvent('command', { detail: { action : 'upgrade', target: addedPanel } });
If target
is undefined, the action will be cancelled.
var ce = new CustomEvent( 'command', {
detail: {
action: 'open'
}
});
document.querySelector('#my-accordion').dispatchEvent(ce);
var panel3 = document.querySelector('#my-accordion .mdlext-accordion__panel:nth-child(3) .mdlext-accordion__tab');
var ce = new CustomEvent('command', {
detail: {
action: 'toggle',
target: panel3
}
});
document.querySelector('#my-accordion').dispatchEvent(ce);
var panel =
'<li class="mdlext-accordion__panel">'
+ '<header class="mdlext-accordion__tab" aria-expanded="true">'
+ '<span class="mdlext-accordion__tab__caption">New Tab</span>'
+ '<i class="mdlext-aria-toggle-material-icons"></i>'
+ '</header>'
+ '<section class="mdlext-accordion__tabpanel">'
+ '<h5>New tab content</h5>'
+ '<p>Some content</p>'
+ '</section>'
+'</li>';
var accordion = document.querySelector('#my-accordion');
accordion.insertAdjacentHTML('beforeend', panel);
var theNewPanel = document.querySelector('#my-accordion .mdlext-accordion__panel:last-child');
var ce = new CustomEvent('command', { detail: { action : 'upgrade', target: theNewPanel } });
document.querySelector('#my-accordion').dispatchEvent(ce);
Refer to snippets/accordion.html or the tests for detailed usage.
The accordion emits a custom toggle
event when a panel opens or closes. The event has a detail object with the following structure:
detail: {
state, // "open" or "close"
tab, // the haeder tab element instance that caused the event
tabpanel // the cooresponding tabpanel element instance
}
Set up an event listener to receive the toggle event.
document.querySelector('#my-accordion').addEventListener('toggle', function(e) {
console.log('Accordion toggled. State:', e.detail.state, 'Source:', e.detail.source);
});
Refer to snippets/accordion.html or the tests for detailed usage.
Upgrade a targeted panel with aria attributes and ripple effects. If you add a panel to the accordion after the page has
loaded, you must call upgrade
to notify the accordion component about the newly added panel.
var accordion = document.querySelector('#my-accordion');
var panel3 = document.querySelector('#my-accordion .mdlext-accordion__panel:nth-child(3)');
accordion.MaterialExtAccordion.upgradeTab( panel3 );
Executes an action, targeting a specific tab. The actions corresponds to the custom events defined for this component.
The detail object parameter has the following structure:
detail: {
action, // "open", "close", "toggle" or "upgrade"
target // Target, panel or tab, of action, "undefined" if all panels should be targeted.
// Note: If you send a null target, the action is cancelled
}
Open a targeted tab and it's corresponding tabpanel.
Close a targeted tab and it's corresponding tabpanel.
Toggle a targeted tab. Open or close a targeted tab and it's corresponding tabpanel.
Upgrade a targeted panel with aria attributes and ripple effects. If you add a panel to the accordion after the page has
loaded, you must call upgrade
to notify the accordion component about the newly added panel.
var accordion = document.querySelector('#my-accordion');
accordion.MaterialExtAccordion.command( {action: 'open'} );
var accordion = document.querySelector('#my-accordion');
var panel3 = document.querySelector('#my-accordion .mdlext-accordion__panel:nth-child(3) .mdlext-accordion__tab');
accordion.MaterialExtAccordion.command( {action: 'toggle', target: panel3} );
Refer to snippets/accordion.html or the tests for detailed usage.
The MDLEXT CSS classes apply various predefined visual and behavioral enhancements to the accordion. The table below lists the available classes and their effects.
MDLEXT class | Effect | Remarks |
---|---|---|
mdlext-accordion |
Defines container as an MDL component | Required on "outer" <div> or <ul> element |
mdlext-js-accordion |
Assigns basic MDL behavior to accordion | Required on "outer" <div> or <ul> element |
mdlext-accordion--horizontal |
Horizontal layot of an accordion | Required. The accordion must have one of mdlext-accordion--horizontal or mdlext-accordion--vertical defined |
mdlext-accordion--vertical |
Vertical layot of an accordion | Required. The accordion must have one of mdlext-accordion--horizontal or mdlext-accordion--vertical defined |
mdlext-js-ripple-effect |
Applies ripple click effect to accordion tab header | Optional. Goes on "outer" <ul> or <div> element |
mdlext-js-animation-effect |
Applies animation effect to accordion tab panel | Optional. Goes on "outer" <ul> or <div> element |
mdlext-accordion__panel |
Defines a container for each section of the accordion - the tab and tabpanel element | Required on first inner <div> element or <li> element |
mdlext-accordion__tab |
Defines a tab header for a corresponding tabpanel | Required on <header> or <div> element |
mdlext-accordion__tabpanel |
The content | Required on <section> or <div> element |
The table below lists available attributes and their effects.
Attribute | Description | Remarks |
---|---|---|
aria-multiselectable |
If true, multiple panels may be open simultaneously | Required. Add aria-multiselectable="true" to the mdlext-accordion element to keep multiple panels open at the same time. If not present, the component will set aria-multiselectable="false" during initialization. |
role=tablist |
Component role | Required. Added by component during initialization if not present. |
role=presentation |
Accordion panel role | Required. Added by component during initialization if not present. |
role=tab |
Accordion tab header role | Required. Added by component during initialization if not present. |
aria-expanded |
Accordion tab header attribute. An accordion should manage the expanded/collapsed state of each tab by maintain its aria-expanded state. | Required. Defaults to aria-expanded="false" . Set aria-expanded="true" if you want a tab to open during page load. |
aria-selected |
Accordion tab header attribute. An accordion should manage the selected state of each tab by maintaining its aria-selected state | Optional. Added by component. |
disabled |
Accordion tab header attribute. Indicates a disabled tab and tabpanel | Optional. If this attribute is present, the tabpanel will not open or close. |
role=tabpanel |
Accordion tabpanel role. | Required. Added by component during initialization if not present. |
aria-hidden |
Accordion tabpanel attribute. An accordion should convey the visibility of each tabpanel by maintaining its aria-hidden state | Required. Added by component. |
hidden |
Accordion tabpanel attribute. | Required. Added by component if aria-hidden="true" . |
The Accordion component is based on / inspired by this CodePen