From 4372458bd85fe4b0450de0e14f146438c79a0940 Mon Sep 17 00:00:00 2001
From: Jon Gunderson
Date: Tue, 21 Sep 2021 13:00:46 -0500
Subject: [PATCH 01/37] updated reference table
---
examples/index.html | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/examples/index.html b/examples/index.html
index c3a4039ea7..3dfb3547d8 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -184,7 +184,7 @@ Examples by Role
Editable Combobox With List Autocomplete
Editable Combobox without Autocomplete
Select-Only Combobox
- Collapsible Dropdown Listbox
+ (Deprecated) Collapsible Dropdown Listbox
Listbox with Grouped Options
Listboxes with Rearrangeable Options
Scrollable Listbox
@@ -276,7 +276,7 @@ Examples by Role
Editable Combobox With List Autocomplete
Editable Combobox without Autocomplete
Select-Only Combobox
- Collapsible Dropdown Listbox
+ (Deprecated) Collapsible Dropdown Listbox
Listbox with Grouped Options
Listboxes with Rearrangeable Options
Scrollable Listbox
@@ -443,7 +443,7 @@ Examples By Properties and States
Editable Combobox without Autocomplete
Select-Only Combobox
Editable Combobox with Grid Popup
- Collapsible Dropdown Listbox
+ (Deprecated) Collapsible Dropdown Listbox
Listbox with Grouped Options
Listboxes with Rearrangeable Options
Scrollable Listbox
@@ -569,7 +569,7 @@ Examples By Properties and States
Disclosure (Show/Hide) for Image Description (HC )
Disclosure Navigation Menu with Top-Level Links
Disclosure Navigation Menu (HC )
- Collapsible Dropdown Listbox
+ (Deprecated) Collapsible Dropdown Listbox
Actions Menu Button Using aria-activedescendant (HC )
Actions Menu Button Using element.focus() (HC )
Navigation Menu Button (HC )
@@ -589,7 +589,7 @@ Examples By Properties and States
Date Picker Combobox (HC )
Editable Combobox with Grid Popup
- Collapsible Dropdown Listbox
+ (Deprecated) Collapsible Dropdown Listbox
Actions Menu Button Using aria-activedescendant (HC )
Actions Menu Button Using element.focus() (HC )
Navigation Menu Button (HC )
@@ -651,7 +651,7 @@ Examples By Properties and States
Feed
Data Grid
Layout Grid
- Collapsible Dropdown Listbox
+ (Deprecated) Collapsible Dropdown Listbox
Listbox with Grouped Options
Listboxes with Rearrangeable Options
Scrollable Listbox
@@ -784,7 +784,7 @@ Examples By Properties and States
Select-Only Combobox
Editable Combobox with Grid Popup
Date Picker Dialog (HC )
- Collapsible Dropdown Listbox
+ (Deprecated) Collapsible Dropdown Listbox
Listbox with Grouped Options
Listboxes with Rearrangeable Options
Scrollable Listbox
From 8d6a0ce0f2c36a63ac75b94e1ea493fe300bc24a Mon Sep 17 00:00:00 2001
From: Jon Gunderson
Date: Tue, 4 Jan 2022 16:45:54 -0600
Subject: [PATCH 02/37] updated tabs examples
---
aria-practices.html | 4 +-
examples/index.html | 20 +-
examples/tabs/css/tabs.css | 104 ++-----
examples/tabs/js/tabs-automatic.js | 138 ++++++++++
examples/tabs/js/tabs-manual.js | 138 ++++++++++
examples/tabs/tabs-1/js/tabs.js | 257 ------------------
examples/tabs/tabs-2/js/tabs.js | 222 ---------------
.../{tabs-1/tabs.html => tabs-automatic.html} | 145 ++++++----
.../{tabs-2/tabs.html => tabs-manual.html} | 121 ++++++---
...{tabs_tabs-1.js => tabs_tabs-automatic.js} | 87 +-----
.../{tabs_tabs-2.js => tabs_tabs-manual.js} | 87 +-----
11 files changed, 504 insertions(+), 819 deletions(-)
create mode 100644 examples/tabs/js/tabs-automatic.js
create mode 100644 examples/tabs/js/tabs-manual.js
delete mode 100644 examples/tabs/tabs-1/js/tabs.js
delete mode 100644 examples/tabs/tabs-2/js/tabs.js
rename examples/tabs/{tabs-1/tabs.html => tabs-automatic.html} (63%)
rename examples/tabs/{tabs-2/tabs.html => tabs-manual.html} (69%)
rename test/tests/{tabs_tabs-1.js => tabs_tabs-automatic.js} (83%)
rename test/tests/{tabs_tabs-2.js => tabs_tabs-manual.js} (83%)
diff --git a/aria-practices.html b/aria-practices.html
index e379122a00..0f3f1f79fe 100644
--- a/aria-practices.html
+++ b/aria-practices.html
@@ -2772,8 +2772,8 @@ Tabs
diff --git a/examples/index.html b/examples/index.html
index 5b35cedb86..d35323b4c2 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -379,8 +379,7 @@ Examples by Role
@@ -393,8 +392,7 @@ Examples by Role
@@ -403,8 +401,7 @@ Examples by Role
@@ -532,8 +529,7 @@ Examples By Properties and States
Actions Menu Button Using aria-activedescendant (HC )
Actions Menu Button Using element.focus() (HC )
Navigation Menu Button (HC )
- Tabs with Automatic Activation
- Tabs with Manual Activation
+ Tabs with Automatic Activation (HC )
Toolbar
@@ -655,8 +651,6 @@ Examples By Properties and States
Horizontal Multi-Thumb Slider (HC )
Date Picker Spin Button
Table
- Tabs with Automatic Activation
- Tabs with Manual Activation
Toolbar
Treegrid Email Inbox
Navigation Treeview (HC )
@@ -695,8 +689,7 @@ Examples By Properties and States
Vertical Temperature Slider (HC )
Date Picker Spin Button
Switch Using HTML Button (HC )
- Tabs with Automatic Activation
- Tabs with Manual Activation
+ Tabs with Automatic Activation (HC )
File Directory Treeview Using Computed Properties
File Directory Treeview Using Declared Properties
Navigation Treeview (HC )
@@ -816,8 +809,7 @@ Examples By Properties and States
Listbox with Grouped Options
Listboxes with Rearrangeable Options
Scrollable Listbox
- Tabs with Automatic Activation
- Tabs with Manual Activation
+ Tabs with Automatic Activation (HC )
File Directory Treeview Using Computed Properties
File Directory Treeview Using Declared Properties
diff --git a/examples/tabs/css/tabs.css b/examples/tabs/css/tabs.css
index dfbfb8fe2d..c3215dedb9 100644
--- a/examples/tabs/css/tabs.css
+++ b/examples/tabs/css/tabs.css
@@ -1,107 +1,63 @@
.tabs {
- width: 20em;
font-family: "lucida grande", sans-serif;
}
[role="tablist"] {
- margin: 0 0 -0.1em;
- overflow: visible;
+ min-width: 550px;
}
-[role="tab"] {
+[role="tab"],
+[role="tab"]:focus,
+[role="tab"]:hover {
position: relative;
+ z-index: 2;
+ top: 2px;
margin: 0;
- padding: 0.3em 0.5em 0.4em;
+ padding: 3px 3px 4px;
border: 1px solid hsl(219deg 1% 72%);
- border-radius: 0.2em 0.2em 0 0;
- box-shadow: 0 0 0.2em hsl(219deg 1% 72%);
+ border-bottom: 2px solid hsl(219deg 1% 72%);
+ border-radius: 5px 5px 0 0;
overflow: visible;
- font-family: inherit;
- font-size: inherit;
background: hsl(220deg 20% 94%);
-}
-
-[role="tab"]:hover::before,
-[role="tab"]:focus::before,
-[role="tab"][aria-selected="true"]::before {
- position: absolute;
- bottom: 100%;
- right: -1px;
- left: -1px;
- border-radius: 0.2em 0.2em 0 0;
- border-top: 3px solid hsl(20deg 96% 48%);
- content: "";
+ outline: none;
+ font-weight: bold;
}
[role="tab"][aria-selected="true"] {
- border-radius: 0;
- background: hsl(220deg 43% 99%);
- outline: 0;
-}
-
-[role="tab"][aria-selected="true"]:not(:focus):not(:hover)::before {
- border-top: 5px solid hsl(218deg 96% 48%);
-}
-
-[role="tab"][aria-selected="true"]::after {
- position: absolute;
- z-index: 3;
- bottom: -1px;
- right: 0;
- left: 0;
- height: 0.3em;
+ padding: 2px 2px 4px;
+ border-width: 2px;
+ border-bottom-color: hsl(220deg 43% 99%);
background: hsl(220deg 43% 99%);
- box-shadow: none;
- content: "";
}
-[role="tab"]:hover,
-[role="tab"]:focus,
-[role="tab"]:active {
- outline: 0;
- border-radius: 0;
- color: inherit;
+[role="tab"] span.focus {
+ display: inline-block;
+ margin: 2px;
+ padding: 4px 6px;
}
-[role="tab"]:hover::before,
-[role="tab"]:focus::before {
- border-color: hsl(20deg 96% 48%);
+[role="tab"]:hover span.focus,
+[role="tab"]:focus span.focus,
+[role="tab"]:active span.focus {
+ padding: 2px 4px;
+ border: 2px solid rgb(36 116 214);
+ border-radius: 3px;
}
[role="tabpanel"] {
- position: relative;
- z-index: 2;
- padding: 0.5em 0.5em 0.7em;
- border: 1px solid hsl(219deg 1% 72%);
- border-radius: 0 0.2em 0.2em;
- box-shadow: 0 0 0.2em hsl(219deg 1% 72%);
+ padding: 5px;
+ border: 2px solid hsl(219deg 1% 72%);
+ border-radius: 0 5px 5px;
background: hsl(220deg 43% 99%);
+ min-height: 10em;
+ min-width: 550px;
+ overflow: auto;
}
[role="tabpanel"].is-hidden {
display: none;
}
-[role="tabpanel"]:focus {
- border-color: hsl(20deg 96% 48%);
- box-shadow: 0 0 0.2em hsl(20deg 96% 48%);
- outline: 0;
-}
-
-[role="tabpanel"]:focus::after {
- position: absolute;
- bottom: 0;
- right: -1px;
- left: -1px;
- border-bottom: 3px solid hsl(20deg 96% 48%);
- border-radius: 0 0 0.2em 0.2em;
- content: "";
-}
-
[role="tabpanel"] p {
margin: 0;
}
-
-[role="tabpanel"] * + p {
- margin-top: 1em;
-}
diff --git a/examples/tabs/js/tabs-automatic.js b/examples/tabs/js/tabs-automatic.js
new file mode 100644
index 0000000000..fe1ad21430
--- /dev/null
+++ b/examples/tabs/js/tabs-automatic.js
@@ -0,0 +1,138 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: tabs-automatic.js
+ *
+ * Desc: Tablist widget that implements ARIA Authoring Practices
+ */
+
+'use strict';
+
+class TabsAutomatic {
+ constructor(groupNode) {
+ this.tablistNode = groupNode;
+
+ this.tabs = [];
+
+ this.firstTab = null;
+ this.lastTab = null;
+
+ this.tabs = Array.from(this.tablistNode.querySelectorAll('[role=tab]'));
+ this.tabpanels = [];
+
+ for (var i = 0; i < this.tabs.length; i += 1) {
+ var tab = this.tabs[i];
+ var tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
+
+ tab.tabIndex = -1;
+ tab.setAttribute('aria-selected', 'false');
+ this.tabpanels.push(tabpanel);
+
+ tab.addEventListener('keydown', this.handleKeydown.bind(this));
+ tab.addEventListener('click', this.handleClick.bind(this));
+
+ if (!this.firstTab) {
+ this.firstTab = tab;
+ }
+ this.lastTab = tab;
+ }
+
+ this.setSelectedTab(this.firstTab, false);
+ }
+
+ setSelectedTab(currentTab, setFocus) {
+ if (typeof setFocus !== 'boolean') {
+ setFocus = true;
+ }
+ for (var i = 0; i < this.tabs.length; i += 1) {
+ var tab = this.tabs[i];
+ if (currentTab === tab) {
+ tab.setAttribute('aria-selected', 'true');
+ tab.removeAttribute('tabindex');
+ this.tabpanels[i].classList.remove('is-hidden');
+ if (setFocus) {
+ tab.focus();
+ }
+ } else {
+ tab.setAttribute('aria-selected', 'false');
+ tab.tabIndex = -1;
+ this.tabpanels[i].classList.add('is-hidden');
+ }
+ }
+ }
+
+ setSelectedToPreviousTab(currentTab) {
+ var index;
+
+ if (currentTab === this.firstTab) {
+ this.setSelectedTab(this.lastTab);
+ } else {
+ index = this.tabs.indexOf(currentTab);
+ this.setSelectedTab(this.tabs[index - 1]);
+ }
+ }
+
+ setSelectedToNextTab(currentTab) {
+ var index;
+
+ if (currentTab === this.lastTab) {
+ this.setSelectedTab(this.firstTab);
+ } else {
+ index = this.tabs.indexOf(currentTab);
+ this.setSelectedTab(this.tabs[index + 1]);
+ }
+ }
+
+ /* EVENT HANDLERS */
+
+ handleKeydown(event) {
+ var tgt = event.currentTarget,
+ flag = false;
+
+ switch (event.key) {
+ case 'ArrowUp':
+ case 'ArrowLeft':
+ this.setSelectedToPreviousTab(tgt);
+ flag = true;
+ break;
+
+ case 'ArrowDown':
+ case 'ArrowRight':
+ this.setSelectedToNextTab(tgt);
+ flag = true;
+ break;
+
+ case 'Home':
+ this.setSelectedTab(this.firstTab);
+ flag = true;
+ break;
+
+ case 'End':
+ this.setSelectedTab(this.lastTab);
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ }
+
+ handleClick(event) {
+ this.setSelectedTab(event.currentTarget);
+ }
+}
+
+// Initialize tablist
+
+window.addEventListener('load', function () {
+ var tablists = document.querySelectorAll('[role=tablist].automatic');
+ for (var i = 0; i < tablists.length; i++) {
+ new TabsAutomatic(tablists[i]);
+ }
+});
diff --git a/examples/tabs/js/tabs-manual.js b/examples/tabs/js/tabs-manual.js
new file mode 100644
index 0000000000..c6e682b9fd
--- /dev/null
+++ b/examples/tabs/js/tabs-manual.js
@@ -0,0 +1,138 @@
+/*
+ * This content is licensed according to the W3C Software License at
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ * File: tabs-manual.js
+ *
+ * Desc: Tablist widget that implements ARIA Authoring Practices
+ */
+
+'use strict';
+
+class TabsAutomatic {
+ constructor(groupNode) {
+ this.tablistNode = groupNode;
+
+ this.tabs = [];
+
+ this.firstTab = null;
+ this.lastTab = null;
+
+ this.tabs = Array.from(this.tablistNode.querySelectorAll('[role=tab]'));
+ this.tabpanels = [];
+
+ for (var i = 0; i < this.tabs.length; i += 1) {
+ var tab = this.tabs[i];
+ var tabpanel = document.getElementById(tab.getAttribute('aria-controls'));
+
+ tab.tabIndex = -1;
+ tab.setAttribute('aria-selected', 'false');
+ this.tabpanels.push(tabpanel);
+
+ tab.addEventListener('keydown', this.handleKeydown.bind(this));
+ tab.addEventListener('click', this.handleClick.bind(this));
+
+ if (!this.firstTab) {
+ this.firstTab = tab;
+ }
+ this.lastTab = tab;
+ }
+
+ this.setSelectedTab(this.firstTab);
+ }
+
+ setSelectedTab(currentTab) {
+ for (var i = 0; i < this.tabs.length; i += 1) {
+ var tab = this.tabs[i];
+ if (currentTab === tab) {
+ tab.setAttribute('aria-selected', 'true');
+ tab.removeAttribute('tabindex');
+ this.tabpanels[i].classList.remove('is-hidden');
+ } else {
+ tab.setAttribute('aria-selected', 'false');
+ tab.tabIndex = -1;
+ this.tabpanels[i].classList.add('is-hidden');
+ }
+ }
+ }
+
+ moveFocusToTab(currentTab) {
+ currentTab.focus();
+ }
+
+ moveFocusToPreviousTab(currentTab) {
+ var index;
+
+ if (currentTab === this.firstTab) {
+ this.moveFocusToTab(this.lastTab);
+ } else {
+ index = this.tabs.indexOf(currentTab);
+ this.moveFocusToTab(this.tabs[index - 1]);
+ }
+ }
+
+ moveFocusToNextTab(currentTab) {
+ var index;
+
+ if (currentTab === this.lastTab) {
+ this.moveFocusToTab(this.firstTab);
+ } else {
+ index = this.tabs.indexOf(currentTab);
+ this.moveFocusToTab(this.tabs[index + 1]);
+ }
+ }
+
+ /* EVENT HANDLERS */
+
+ handleKeydown(event) {
+ var tgt = event.currentTarget,
+ flag = false;
+
+ switch (event.key) {
+ case 'ArrowUp':
+ case 'ArrowLeft':
+ this.moveFocusToPreviousTab(tgt);
+ flag = true;
+ break;
+
+ case 'ArrowDown':
+ case 'ArrowRight':
+ this.moveFocusToNextTab(tgt);
+ flag = true;
+ break;
+
+ case 'Home':
+ this.moveFocusToTab(this.firstTab);
+ flag = true;
+ break;
+
+ case 'End':
+ this.moveFocusToTab(this.lastTab);
+ flag = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (flag) {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ }
+
+ // Since this example uses buttons for the tabs, the click handler also is activated
+ // with the space and enter keys
+ handleClick(event) {
+ this.setSelectedTab(event.currentTarget);
+ }
+}
+
+// Initialize tablist
+
+window.addEventListener('load', function () {
+ var tablists = document.querySelectorAll('[role=tablist].manual');
+ for (var i = 0; i < tablists.length; i++) {
+ new TabsAutomatic(tablists[i]);
+ }
+});
diff --git a/examples/tabs/tabs-1/js/tabs.js b/examples/tabs/tabs-1/js/tabs.js
deleted file mode 100644
index 9c17ba34ff..0000000000
--- a/examples/tabs/tabs-1/js/tabs.js
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * This content is licensed according to the W3C Software License at
- * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
- */
-
-'use strict';
-
-(function () {
- var tablist = document.querySelectorAll('[role="tablist"]')[0];
- var tabs;
- var panels;
- var delay = determineDelay();
-
- generateArrays();
-
- function generateArrays() {
- tabs = document.querySelectorAll('[role="tab"]');
- panels = document.querySelectorAll('[role="tabpanel"]');
- }
-
- // For easy reference
- var keys = {
- end: 35,
- home: 36,
- left: 37,
- up: 38,
- right: 39,
- down: 40,
- delete: 46,
- };
-
- // Add or subtract depending on key pressed
- var direction = {
- 37: -1,
- 38: -1,
- 39: 1,
- 40: 1,
- };
-
- // Bind listeners
- for (var i = 0; i < tabs.length; ++i) {
- addListeners(i);
- }
-
- function addListeners(index) {
- tabs[index].addEventListener('click', clickEventListener);
- tabs[index].addEventListener('keydown', keydownEventListener);
- tabs[index].addEventListener('keyup', keyupEventListener);
-
- // Build an array with all tabs (s) in it
- tabs[index].index = index;
- }
-
- // When a tab is clicked, activateTab is fired to activate it
- function clickEventListener(event) {
- var tab = event.target;
- activateTab(tab, false);
- }
-
- // Handle keydown on tabs
- function keydownEventListener(event) {
- var key = event.keyCode;
-
- switch (key) {
- case keys.end:
- event.preventDefault();
- // Activate last tab
- activateTab(tabs[tabs.length - 1]);
- break;
- case keys.home:
- event.preventDefault();
- // Activate first tab
- activateTab(tabs[0]);
- break;
-
- // Up and down are in keydown
- // because we need to prevent page scroll >:)
- case keys.up:
- case keys.down:
- determineOrientation(event);
- break;
- }
- }
-
- // Handle keyup on tabs
- function keyupEventListener(event) {
- var key = event.keyCode;
-
- switch (key) {
- case keys.left:
- case keys.right:
- determineOrientation(event);
- break;
- case keys.delete:
- determineDeletable(event);
- break;
- }
- }
-
- // When a tablist’s aria-orientation is set to vertical,
- // only up and down arrow should function.
- // In all other cases only left and right arrow function.
- function determineOrientation(event) {
- var key = event.keyCode;
- var vertical = tablist.getAttribute('aria-orientation') == 'vertical';
- var proceed = false;
-
- if (vertical) {
- if (key === keys.up || key === keys.down) {
- event.preventDefault();
- proceed = true;
- }
- } else {
- if (key === keys.left || key === keys.right) {
- proceed = true;
- }
- }
-
- if (proceed) {
- switchTabOnArrowPress(event);
- }
- }
-
- // Either focus the next, previous, first, or last tab
- // depending on key pressed
- function switchTabOnArrowPress(event) {
- var pressed = event.keyCode;
-
- for (var x = 0; x < tabs.length; x++) {
- tabs[x].addEventListener('focus', focusEventHandler);
- }
-
- if (direction[pressed]) {
- var target = event.target;
- if (target.index !== undefined) {
- if (tabs[target.index + direction[pressed]]) {
- tabs[target.index + direction[pressed]].focus();
- } else if (pressed === keys.left || pressed === keys.up) {
- focusLastTab();
- } else if (pressed === keys.right || pressed == keys.down) {
- focusFirstTab();
- }
- }
- }
- }
-
- // Activates any given tab panel
- function activateTab(tab, setFocus) {
- setFocus = setFocus || true;
- // Deactivate all other tabs
- deactivateTabs();
-
- // Remove tabindex attribute
- tab.removeAttribute('tabindex');
-
- // Set the tab as selected
- tab.setAttribute('aria-selected', 'true');
-
- // Get the value of aria-controls (which is an ID)
- var controls = tab.getAttribute('aria-controls');
-
- // Remove is-hidden class from tab panel to make it visible
- document.getElementById(controls).classList.remove('is-hidden');
-
- // Set focus when required
- if (setFocus) {
- tab.focus();
- }
- }
-
- // Deactivate all tabs and tab panels
- function deactivateTabs() {
- for (var t = 0; t < tabs.length; t++) {
- tabs[t].setAttribute('tabindex', '-1');
- tabs[t].setAttribute('aria-selected', 'false');
- tabs[t].removeEventListener('focus', focusEventHandler);
- }
-
- for (var p = 0; p < panels.length; p++) {
- panels[p].classList.add('is-hidden');
- }
- }
-
- // Make a guess
- function focusFirstTab() {
- tabs[0].focus();
- }
-
- // Make a guess
- function focusLastTab() {
- tabs[tabs.length - 1].focus();
- }
-
- // Detect if a tab is deletable
- function determineDeletable(event) {
- var target = event.target;
-
- if (target.getAttribute('data-deletable') !== null) {
- // Delete target tab
- deleteTab(event, target);
-
- // Update arrays related to tabs widget
- generateArrays();
-
- // Activate the closest tab to the one that was just deleted
- if (target.index - 1 < 0) {
- activateTab(tabs[0]);
- } else {
- activateTab(tabs[target.index - 1]);
- }
- }
- }
-
- // Deletes a tab and its panel
- function deleteTab(event) {
- var target = event.target;
- var panel = document.getElementById(target.getAttribute('aria-controls'));
-
- target.parentElement.removeChild(target);
- panel.parentElement.removeChild(panel);
- }
-
- // Determine whether there should be a delay
- // when user navigates with the arrow keys
- function determineDelay() {
- var hasDelay = tablist.hasAttribute('data-delay');
- var delay = 0;
-
- if (hasDelay) {
- var delayValue = tablist.getAttribute('data-delay');
- if (delayValue) {
- delay = delayValue;
- } else {
- // If no value is specified, default to 300ms
- delay = 300;
- }
- }
-
- return delay;
- }
-
- //
- function focusEventHandler(event) {
- var target = event.target;
-
- setTimeout(checkTabFocus, delay, target);
- }
-
- // Only activate tab on focus if it still has focus after the delay
- function checkTabFocus(target) {
- var focused = document.activeElement;
-
- if (target === focused) {
- activateTab(target, false);
- }
- }
-})();
diff --git a/examples/tabs/tabs-2/js/tabs.js b/examples/tabs/tabs-2/js/tabs.js
deleted file mode 100644
index 8c8d351aaa..0000000000
--- a/examples/tabs/tabs-2/js/tabs.js
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * This content is licensed according to the W3C Software License at
- * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
- */
-
-'use strict';
-
-(function () {
- var tablist = document.querySelectorAll('[role="tablist"]')[0];
- var tabs;
- var panels;
-
- generateArrays();
-
- function generateArrays() {
- tabs = document.querySelectorAll('[role="tab"]');
- panels = document.querySelectorAll('[role="tabpanel"]');
- }
-
- // For easy reference
- var keys = {
- end: 35,
- home: 36,
- left: 37,
- up: 38,
- right: 39,
- down: 40,
- delete: 46,
- enter: 13,
- space: 32,
- };
-
- // Add or subtract depending on key pressed
- var direction = {
- 37: -1,
- 38: -1,
- 39: 1,
- 40: 1,
- };
-
- // Bind listeners
- for (var i = 0; i < tabs.length; ++i) {
- addListeners(i);
- }
-
- function addListeners(index) {
- tabs[index].addEventListener('click', clickEventListener);
- tabs[index].addEventListener('keydown', keydownEventListener);
- tabs[index].addEventListener('keyup', keyupEventListener);
-
- // Build an array with all tabs (s) in it
- tabs[index].index = index;
- }
-
- // When a tab is clicked, activateTab is fired to activate it
- function clickEventListener(event) {
- var tab = event.target;
- activateTab(tab, false);
- }
-
- // Handle keydown on tabs
- function keydownEventListener(event) {
- var key = event.keyCode;
-
- switch (key) {
- case keys.end:
- event.preventDefault();
- // Activate last tab
- focusLastTab();
- break;
- case keys.home:
- event.preventDefault();
- // Activate first tab
- focusFirstTab();
- break;
-
- // Up and down are in keydown
- // because we need to prevent page scroll >:)
- case keys.up:
- case keys.down:
- determineOrientation(event);
- break;
- }
- }
-
- // Handle keyup on tabs
- function keyupEventListener(event) {
- var key = event.keyCode;
-
- switch (key) {
- case keys.left:
- case keys.right:
- determineOrientation(event);
- break;
- case keys.delete:
- determineDeletable(event);
- break;
- case keys.enter:
- case keys.space:
- activateTab(event.target);
- break;
- }
- }
-
- // When a tablist’s aria-orientation is set to vertical,
- // only up and down arrow should function.
- // In all other cases only left and right arrow function.
- function determineOrientation(event) {
- var key = event.keyCode;
- var vertical = tablist.getAttribute('aria-orientation') == 'vertical';
- var proceed = false;
-
- if (vertical) {
- if (key === keys.up || key === keys.down) {
- event.preventDefault();
- proceed = true;
- }
- } else {
- if (key === keys.left || key === keys.right) {
- proceed = true;
- }
- }
-
- if (proceed) {
- switchTabOnArrowPress(event);
- }
- }
-
- // Either focus the next, previous, first, or last tab
- // depending on key pressed
- function switchTabOnArrowPress(event) {
- var pressed = event.keyCode;
-
- if (direction[pressed]) {
- var target = event.target;
- if (target.index !== undefined) {
- if (tabs[target.index + direction[pressed]]) {
- tabs[target.index + direction[pressed]].focus();
- } else if (pressed === keys.left || pressed === keys.up) {
- focusLastTab();
- } else if (pressed === keys.right || pressed == keys.down) {
- focusFirstTab();
- }
- }
- }
- }
-
- // Activates any given tab panel
- function activateTab(tab, setFocus) {
- setFocus = setFocus || true;
- // Deactivate all other tabs
- deactivateTabs();
-
- // Remove tabindex attribute
- tab.removeAttribute('tabindex');
-
- // Set the tab as selected
- tab.setAttribute('aria-selected', 'true');
-
- // Get the value of aria-controls (which is an ID)
- var controls = tab.getAttribute('aria-controls');
-
- // Remove is-hidden class from tab panel to make it visible
- document.getElementById(controls).classList.remove('is-hidden');
-
- // Set focus when required
- if (setFocus) {
- tab.focus();
- }
- }
-
- // Deactivate all tabs and tab panels
- function deactivateTabs() {
- for (var t = 0; t < tabs.length; t++) {
- tabs[t].setAttribute('tabindex', '-1');
- tabs[t].setAttribute('aria-selected', 'false');
- }
-
- for (var p = 0; p < panels.length; p++) {
- panels[p].classList.add('is-hidden');
- }
- }
-
- // Make a guess
- function focusFirstTab() {
- tabs[0].focus();
- }
-
- // Make a guess
- function focusLastTab() {
- tabs[tabs.length - 1].focus();
- }
-
- // Detect if a tab is deletable
- function determineDeletable(event) {
- var target = event.target;
-
- if (target.getAttribute('data-deletable') !== null) {
- // Delete target tab
- deleteTab(event, target);
-
- // Update arrays related to tabs widget
- generateArrays();
-
- // Activate the closest tab to the one that was just deleted
- if (target.index - 1 < 0) {
- activateTab(tabs[0]);
- } else {
- activateTab(tabs[target.index - 1]);
- }
- }
- }
-
- // Deletes a tab and its panel
- function deleteTab(event) {
- var target = event.target;
- var panel = document.getElementById(target.getAttribute('aria-controls'));
-
- target.parentElement.removeChild(target);
- panel.parentElement.removeChild(panel);
- }
-})();
diff --git a/examples/tabs/tabs-1/tabs.html b/examples/tabs/tabs-automatic.html
similarity index 63%
rename from examples/tabs/tabs-1/tabs.html
rename to examples/tabs/tabs-automatic.html
index abffd180d1..142967f0f3 100644
--- a/examples/tabs/tabs-1/tabs.html
+++ b/examples/tabs/tabs-automatic.html
@@ -6,21 +6,23 @@
-
-
-
-
+
+
+
+
-
+
+
+
@@ -33,7 +35,7 @@ Example of Tabs with Automatic Activation
Similar examples include:
@@ -43,23 +45,76 @@ Example
-
-
Nils Frahm
-
Agnes Obel
-
Joke
+
Selected Danish Composers
+
+
+ Maria Ahlefeldt
+
+
+ Carl Andersen
+
+
+ Ida da Fonseca
+
+
+ Peter Lange-Müller
+
-
-
Nils Frahm is a German musician, composer and record producer based in Berlin. He is known for combining classical and electronic music and for an unconventional approach to the piano in which he mixes a grand piano, upright piano, Roland Juno-60, Rhodes piano, drum machine, and Moog Taurus.
+
+
Maria Theresia Ahlefeldt (16 January 1755 – 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. Maria Theresia composed music for several ballets, operas, and plays of the royal theatre. She was given good critic as a composer and described as a “virkelig Tonekunstnerinde ” ('a True Artist of Music').
-
-
-
Agnes Caroline Thaarup Obel is a Danish singer/songwriter. Her first album, Philharmonics, was released by PIAS Recordings on 4 October 2010 in Europe. Philharmonics was certified gold in June 2011 by the Belgian Entertainment Association (BEA) for sales of 10,000 Copies.
+
+
Carl Joachim Andersen (29 April 1847 – 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. Both as a virtuoso and as composer of flute music, he is considered one of the best of his time. He was considered to be a tough leader and teacher and demanded as such a lot from his orchestras but through that style he reached a high level.
-
-
-
Fear of complicated buildings:
-
A complex complex complex.
+
+
Ida Henriette da Fonseca (July 27, 1802 – July 6, 1858) was a Danish opera singer and composer. Ida Henriette da Fonseca was the daughter of Abraham da Fonseca (1776–1849) and Marie Sofie Kiærskou (1784–1863). She and her sister Emilie da Fonseca were students of Giuseppe Siboni, choir master of the Opera in Copenhagen. She was given a place at the royal Opera alongside her sister the same year she debuted in 1827.
+
+
+
Peter Erasmus Lange-Müller (1 December 1850 – 26 February 1926) was a Danish composer and pianist. His compositional style was influenced by Danish folk music and by the work of Robert Schumann; Johannes Brahms; and his Danish countrymen, including J.P.E. Hartmann.
@@ -68,10 +123,10 @@
Example
Accessibility Features
-
- To demonstrate the effects of deleting a tab, the third tab, labeled Joke ,
- can be deleted when it has focus by pressing Delete .
-
+
+ The selected tab
has a 2 pixel border when it is selected to make the selected tab discernable from the other tabs to people wiith visual impairments using operating system high contrast modes.
+ A span
element is used to create a focus outline around the text to make the focus disernable to people with visual impairments using operating system high contrast modes.
+
@@ -121,10 +176,6 @@ Keyboard Support
End
Moves focus to the last tab and activates it.
-
- Delete
- When focus is on the Joke tab, removes the tab from the tab list and places focus on the previous tab.
-
@@ -151,10 +202,10 @@
Role, Property, State, and Tabindex Attributes
Indicates that the element serves as a container for a set of tabs.
-
+
- aria-label=Entertainment
+ aria-labelledby=IDREF
div
@@ -177,7 +228,7 @@ Role, Property, State, and Tabindex Attributes
to be displayed.
- Provides a title for its associated tabpanel
.
+ Provides a label for its associated tabpanel
.
@@ -192,7 +243,7 @@ Role, Property, State, and Tabindex Attributes
- Indicates the tab control is activated and its associated panel is displayed.
+ Indicates the tab is selected and its associated tabpanel is displayed.
Set when a tab receives focus.
@@ -208,10 +259,10 @@ Role, Property, State, and Tabindex Attributes
- Indicates the tab control is not active and its associated panel is NOT
+ Indicates the tab is not selected and its associated tabpanel is NOT
displayed.
- Set for all tab elements in the tab set except the focused tab.
+ Set for all tab elements in the tab set except the tab that is selected.
@@ -241,7 +292,7 @@
Role, Property, State, and Tabindex Attributes
button
- Refers to the tabpanel
element associated with the tab.
+ Refers to the element with role=tabpanel
associated with the tab.
@@ -278,22 +329,6 @@ Role, Property, State, and Tabindex Attributes
-
-
-
- tabindex=0
-
-
- div
-
-
-
- Puts the tabpanel in the page Tab sequence.
- Facilitates movement to panel content for assistive technology users.
- Especially helpful if there are panels that do not contain a focusable element.
-
-
-
@@ -301,8 +336,8 @@
Role, Property, State, and Tabindex Attributes
Javascript and CSS Source Code
@@ -323,7 +358,5 @@
HTML Source Code
Tabs Design Pattern in WAI-ARIA Authoring Practices 1.2
-
-