diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..942aa34 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [1.1.0] - 2020-01-11 + +### Added + +- Option to have all `tab` elements in the normal tabbing order, instead of just one. +- Option to ensure `tabpanel` elements are in the normal tabbing order. +- Option to automatically activate/deactivate tabs when moving focus between them using the arrow keys. +- Option to control the `tabindex` value that's added to `tab` and `tabpanel` elements, to account for custom tabbing orders on a page. +- Prevent `tab` activation or de-activation if it has `aria-disabled="true"` set (the API's `open` and `close` methods deliberately ignore this). +- Setting `aria-hidden="true"` as well as `hidden="hidden"` when hiding a `tabpanel`. + +### Changed + +- Set `aria-expanded="true"` on the active `tab` and `tabpanel` elements, instead of just the `tab`. This was originally being set on just the `tab` because NVDA wasn't announcing it on the `tabpanel`, however the WCAG spec does specifically indicate setting it on the `tabpanel`. This change should be a good compromise to be announced by NVDA, whilst also being compliant with the WCAG spec. +- When initialising, and an associated panel is not found for a `tab`, the `role` attribute will be removed if the element has `role="tab"` set. Previously the `role`, `tabindex`, `aria-controls`, `aria-selected`, and `aria-expanded` attributes were all being removed from it regardless, but that risked breaking other functionality on the page unrelated to the tablist. This change should be sufficient to prevent screen reader confusion related to the tablist, without affecting any other page functionality reliant on the processed element(s). + +### Fixed + +- Issue where no tabs were focusable after using the API to programmatically close them all. +- Issue where, when deleting the first `tab`, page focus was moved to the last `tab`. diff --git a/README.md b/README.md index d0a59eb..93db7c9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![npm version](https://img.shields.io/npm/v/aria-tablist.svg)](http://npm.im/aria-tablist) [![gzip size](http://img.badgesize.io/https://unpkg.com/aria-tablist/dist/aria-tablist.min.js?compression=gzip)](https://unpkg.com/aria-tablist/dist/aria-tablist.min.js) -Dependency-free plain JavaScript module for WCAG compliant tablists +Dependency-free plain JavaScript module for WCAG compliant tablists. Also great for accordions. [Try out the examples](https://mynamesleon.github.io/aria-tablist/examples/) @@ -36,17 +36,19 @@ Or grab the minified JavaScript from unpkg: ``` +The module relies entirely on standard attributes: it sets the `role` on elements if it needs to, `aria-` attributes for indicating behaviour to screen readers, and relies on setting and removing `hidden="hidden"` to toggle element visibility. This means that you can use all of your own class names and styling, and the module won't override them. + ## HTML Requirements / Progressive Enhancement When the module is called on an element, the following steps are taken: -1. The module will look for elements with `role="tab"` set -2. If none are found, all direct children will be processed -3. For each tab (assumed or otherwise) the module will check for a matching panel by: - 1. Checking for an `aria-controls` attribute on the tab, and searching for an element with a matching ID - 2. If the tab has an `id`, searching for an element with an `aria-labelledby` attribute that matches that `id` -4. For any tabs without a matching panel, any applicable ARIA attributes will be removed from it -5. For the tabs that have associated panels, all relevant ARIA attributes will be set automatically +1. The module will look for elements with `role="tab"` set. +2. If none are found, all direct children will be processed. +3. For each assumed `tab`, the module will check for a matching `tabpanel` by: + 1. Checking for an `aria-controls` attribute on the `tab`, and searching for an element with a matching `id`. + 2. If the `tab` has an `id`, searching for an element with an `aria-labelledby` attribute that matches that `id`. +4. For any tabs that were processed where a matching panel was **not** found, if they had `role="tab"` set, the `role` attribute will be removed to prevent confusion to screen reader users. +5. The found tabs and associated panels will then have the relevant `role` and `aria-` 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: @@ -60,6 +62,10 @@ This means your HTML only needs to indicate the relationship between the tabs an
@@ -275,20 +298,96 @@+ +<script> + new AriaTablist(document.getElementById('fruits'), { + focusableTabs: true + }); +</script>Vertical tabs
<div id="pear">Pear</div> <div aria-labelledby="pear">...</div> <div> -
+<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> + new AriaTablist(document.getElementById('fruits'), { + arrowActivation: true + }); +</script>+
+<div id="fruits"> + <div aria-controls="apple">Apple</div> + <div aria-controls="orange" aria-disabled="true" aria-selected="true"> + Orange + </div> + <div aria-controls="pear">Pear</div> + + <div id="apple">...</div> + <div id="orange">...</div> + <div id="pear">...</div> +<div> + +<script> + new AriaTablist(document.getElementById('fruits')); +</script>
role="tab"
set
+ role="tab"
set.
tab
, the module
+ will check for a matching
+ tabpanel
by:
aria-controls
attribute on
- the tab, and searching for an element
- with a matching ID
+ the tab
, and searching for
+ an element with a matching
+ id
.
id
,
searching for an element with an
aria-labelledby
attribute
- that matches that id
+ that matches that id
.
role="tab"
set, the
+ role
attribute will be removed to
+ prevent confusion to screen reader users.
role
and
+ aria-
attributes set automatically.
@@ -420,8 +598,8 @@
So if you need to cater for users without JavaScript, or if the JavaScript fails to load for @@ -434,16 +612,22 @@
-<div id="tabs" role="tablist" aria-label="Tabs"> - <div role="tab" tabindex="-1" aria-controls="panel-1" id="tab-1">Panel 1</div> - <div role="tab" tabindex="0" aria-selected="true" aria-controls="panel-2" id="tab-2">Panel 2</div> - <div role="tab" tabindex="-1" aria-controls="panel-3" id="tab-3">Panel 3</div> +<div role="tablist" aria-label="Tabs" aria-orientation="horizontal"> + <div role="tab" tabindex="-1" aria-controls="panel-1" id="tab-1"> + Panel 1 + </div> + <div role="tab" tabindex="0" aria-selected="true" aria-controls="panel-2" id="tab-2"> + Panel 2 + </div> + <div role="tab" tabindex="-1" aria-controls="panel-3" id="tab-3"> + Panel 3 + </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> -+<div role="tabpanel" aria-labelledby="tab-3" hidden="hidden" id="panel-3">...</div>
Numer
Number
0
@@ -463,18 +647,45 @@ Boolean
data-deletable="false"
false
+ Boolean
tabindex="0"
on
+ them), instead of just 1false
+ Boolean
tabindex="0"
on them)true
+ Boolean
false
Number
0
+ Function
(panel) => {}
+ (panel, tab) => {}
Function
(panel) => {}
+ (panel, tab) => {}
Function
(index: Number|Element): void
+ (index: Number|Element, focusTab: Boolean =
+ false): void