Examples
1. Horizontal tabs (standard example)
<div id="fruits">
<div aria-controls="apple">Apple</div>
<div aria-controls="orange">Orange</div>
<div aria-controls="pear">Pear</div>
<div>
<div id="apple">...</div>
<div id="orange">...</div>
<div id="pear">...</div>
<script>
AriaTablist(document.getElementById('fruits'));
</script>
As per the WCAG spec, by default, only the
active tab is in the page's normal tabbing order; keyboard-only users can
navigate to the others using the arrow keys, or you can set the
focusableTabs
option to true
(in the next example) to ensure
all tabs are in the page's normal tabbing order. The 'home' and 'end' keys will also
move focus to the first and last tab respectively.
2. Vertical tabs, all focusable
This example makes for a good accordion...
<div id="fruits" aria-orientation="vertical">
<div id="apple">Apple</div>
<div aria-labelledby="apple">...</div>
<div id="orange">Orange</div>
<div aria-labelledby="orange">...</div>
<div id="pear">Pear</div>
<div aria-labelledby="pear">...</div>
<div>
<script>
AriaTablist(document.getElementById('fruits'), {
focusableTabs: true
});
</script>
3. Horizontal tabs that activate as you navigate through them with any arrow keypresses
<div id="fruits">
<div aria-controls="apple">Apple</div>
<div aria-controls="orange">Orange</div>
<div aria-controls="pear">Pear</div>
<div>
<div id="apple">...</div>
<div id="orange">...</div>
<div id="pear">...</div>
<script>
AriaTablist(document.getElementById('fruits'), {
arrowActivation: true,
allArrows: true
});
</script>
4. Vertical, all focusable, deletable, multi-selectable tabs, with second and third tab active by default
The deletable
option enables tab deletion via the keyboard
<div id="fruits" aria-multiselectable="true" aria-orientation="vertical">
<div aria-controls="apple">Apple</div>
<div id="apple">...</div>
<div aria-controls="orange" aria-selected="true">Orange</div>
<div id="orange">...</div>
<div aria-controls="pear" aria-selected="true">Pear</div>
<div id="pear">...</div>
<div>
<script>
AriaTablist(document.getElementById('fruits'), {
deletable: true,
focusableTabs: true
});
</script>
5. Horizontal tabs, with second tab open by default but disabled, in RTL mode
-
Apple
-
Orange
-
Pear
<ul id="fruits" dir="rtl">
<li>
<div data-controls="apple">Apple</div>
</li>
<li>
<div data-controls="orange" aria-disabled="true" data-selected="true">
Orange
</div>
</li>
<li>
<div aria-controls="pear">Pear</div>
</li>
</ul>
<div id="apple">...</div>
<div id="orange">...</div>
<div id="pear">...</div>
<script>
AriaTablist(document.getElementById('fruits'), {
tabSelector: 'div'
});
</script>
With dir="rtl"
set on the main element, or on the
document
(<html>
tag), the horizontal arrow key
behaviour will also be reversed (the left arrow will move focus to the next tab, and
the right arrow will move focus to the previous).
This example also demonstrates that the HTML structure can be completely flexible, that
data-controls
can be used to indicate the tab-panel relationships as well
as aria-controls
, and that data-selected
can be used to
indicate the starting tab(s) as well as aria-selected
.
HTML requirements / Progressive enhancement
When the module is called on an element, the following steps are taken:
-
The module will search for
tab
elements using thetabSelector
option ('[role="tab"]'
by default). - If none are found, all direct children will be processed.
-
For each assumed
tab
, the module will check for a matchingtabpanel
by:-
Checking for an
aria-controls
ordata-controls
attribute on thetab
, and searching for an element with a matchingid
. -
If the tab has an
id
, searching for an element with anaria-labelledby
ordata-labelledby
attribute that matches thatid
.
-
Checking for an
-
For any tabs that were processed where a matching panel was
not found, if they have
role="tab"
set, therole
attribute will be removed to prevent confusion to screen reader users. -
The found tabs and associated panels will then have the relevant
role
andaria-
attributes set automatically.
This means your HTML only needs to indicate the relationship between the tabs and panels, and the module will handle the rest:
<div id="tabs">
<div id="tab-1">Panel 1</div>
<div aria-controls="panel-2">Panel 2</div>
<div data-controls="panel-3">Panel 3</div>
<div>
<div aria-labelledby="tab-1">...</div>
<div id="panel-2">...</div>
<div id="panel-3">...</div>
<script>
AriaTablist(document.getElementById('tabs'));
</script>
So if you need to cater for users without JavaScript, or if the JavaScript fails to load for whatever reason, there won't be any applicable roles set that would confuse a screen reader user.
You can of course include all of the optimal ARIA attributes straight away if you wish, including indicating which tab should be active by default:
<div role="tablist" aria-orientation="horizontal" aria-label="Fruits">
<div role="tab" tabindex="-1" aria-controls="panel-1" id="tab-1">
Apple
</div>
<div role="tab" tabindex="0" aria-selected="true" aria-controls="panel-2" id="tab-2">
Orange
</div>
<div role="tab" tabindex="-1" aria-controls="panel-3" id="tab-3">
Pear
</div>
<div>
<div role="tabpanel" aria-labelledby="tab-1" hidden="hidden" id="panel-1">...</div>
<div role="tabpanel" aria-labelledby="tab-2" id="panel-2">...</div>
<div role="tabpanel" aria-labelledby="tab-3" hidden="hidden" id="panel-3">...</div>
Options
Most of the functionality is assumed from the included ARIA attributes in your HTML, but some basic options are also available:
- delay:
number
-
Delay in milliseconds before showing tab(s) from user interaction
Default:0
- deletable:
Boolean
-
Allow tab deletion via the keyboard - can be overridden per tab by setting
data-deletable="false"
Default:false
- focusableTabs:
Boolean
-
Make all tabs focusable in the normal tabbing order (by setting a
tabindex
on them), instead of just one
Default:false
- focusablePanels:
Boolean
-
Make all tab panels focusable in the normal tabbing order (by setting a
tabindex
on them)
Default:true
- arrowActivation:
Boolean
-
Enable all arrow keys for moving focus between the tabs
Default:false
- allArrows:
Boolean
-
Enable all arrow keys for moving focus, instead of checking the
aria-orientation
value (left and up for previous, right and down for next)
Default:false
- tabSelector:
string
-
the selector to use when initially searching for tab elements; if none are found,
all direct children of the main element will be processed
Default:'[role="tab"]'
- tabindex:
number
-
Value to use when setting tabs or panels to be part of the page's tabbing order
Default:0
- onOpen:
Function
-
Callback each time a tab opens
Default:(panel, tab) => {}
- onClose:
Function
-
Callback each time a tab closes
Default:(panel, tab) => {}
- onDelete:
Function
-
Callback when a tab is deleted
Default:(tab) => {}
- onReady:
Function
-
Callback once ready
Default:(tablist) => {}
All component options that accept a
Function
will have their context (this
) set to include the
full autocomplete API (assuming you use a normal
function: () {}
declaration for the callbacks instead of arrow functions).
API
The returned AriaTablist
instance exposes the following API (which is also
available on the original element's ariaTablist
property):
- tabs:
HTMLElement[]
- The tab elements the module currently recognises
- panels:
HTMLElement[]
- The panel elements the module currently recognises
- options:
AriaTablistOptions
- The current options object
-
open:
(index: number | HTMLElement, focusTab: Boolean = true): void
- Trigger a particular tab to open (by index, or element), even if disabled
-
close:
(index: number | HTMLElement, focusTab: Boolean = false): void
- Trigger a particular tab to close (by index, or element), even if disabled
-
delete:
(index: number | HTMLElement): void
- Delete a particular tab and its corresponding panel (if deletable)
- destroy:
Function
- Destroy the module - does not remove the elements from the DOM