Skip to main content
Workforce LibreTexts

6.6: Tab Panels

  • Page ID
    15624
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)

    ( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\id}{\mathrm{id}}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\kernel}{\mathrm{null}\,}\)

    \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\)

    \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\)

    \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    \( \newcommand{\vectorA}[1]{\vec{#1}}      % arrow\)

    \( \newcommand{\vectorAt}[1]{\vec{\text{#1}}}      % arrow\)

    \( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vectorC}[1]{\textbf{#1}} \)

    \( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)

    \( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)

    \( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)

    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)

    Tab panels, much like accordions, are often used to conserve space and reduce scrolling. They are typically made up of a tablist that contains a series of tabs, each tab controlling the display of a panel. As each tab is activated, its associated panel is displayed and other panels are hidden. When a tab is selected, it is highlighted to indicate which tab and panel are active.

    WAI-ARIA roles, states, and properties used in a tab panel

    • role="tablist"
    • role="tabpanel"
    • role="tab"
    • aria-hidden="[true|false]"
    • tabindex = [0 | -1]
    • aria-controls="[panel id]"
    • aria-selected="[true|false]"
    Suggested Reading: Additional information about creating accessible tab panels can be found in the WAI-ARIA Authoring Practices.

    The following JSFiddle presents a typical tab panel widget. Review the JavaScript and HTML markup. Test the tab panel presented under the Result tab with ChromeVox to understand how it functions without any accessibility features added. You can work in JSFiddle itself by clicking “Edit in JSFiddle”, copying the accessibility/WAI-ARIA code described below to fix the accessibility of the tab panel before completing Activity 10 on the page that follows.

    A link to an interactive elements can be found at the bottom of this page.

    In our case, we are generating the tabs for each child <div> defined in the HTML, though tabs and tab panels could be static HTML. The tablist is made up of a <ul> and child <li> elements. We assign role="tablist" to the <ul> to remove its list semantics and replace it with tab panel semantics.

    A link to an interactive elements can be found at the bottom of this page.

    Next, we add WAI-ARIA to the panels, assigning role="tabpanel" to each of the original <div> elements, hide them by default with aria-hidden="true", and finally adding tabindex="0" to make the panels keyboard focusable.

    A link to an interactive elements can be found at the bottom of this page.

    The tabs themselves are now defined, replacing the list item semantics with tab semantics adding role="tab" to each of the <li> elements generated. We also need to define which tab controls which tabpanel, dynamically generating aria-controls="[panel_id]" for each of the tabs.

    A link to an interactive elements can be found at the bottom of this page.

    When a tab is selected, we want to remove selection from other tabs with aria-selected="false", and remove keyboard access temporarily by assigning tabindex="-1" to the unselected tabs, so that that tabpanel becomes next in the tab order, and users can navigate directly from the tab to the panel without having to pass through the other tabs in the tablist.

    A link to an interactive elements can be found at the bottom of this page.

    Likewise, when a tab is selected we assign aria-selected="true" so screen readers announce the selected tab, we add tabindex="0" as the roving tabindex to make that tab focusable.

    A link to an interactive elements can be found at the bottom of this page.

    As the tabs change, hide all the panels with aria-hidden="true" so screen readers do not see them, then open the panel the current tab controls with aria-hidden="false" so screen readers can see the active panel. These are added to the end of the selectTab() function.

    A link to an interactive elements can be found at the bottom of this page.

    Adding Keyboard Operability

    W3C describes authoring practices for tab panel keyboard interactions as follows.

    Keyboard Interaction for Tab Panels

    For the tab list:

    • Tab: When focus moves into the tab list, places focus on the active tab element. When the tab list contains the focus, moves focus to the next element in the page tab sequence outside the tablist, which is typically either the first focusable element inside the tab panel or the tab panel itself.
    • When focus is on a tab element in a horizontal tab list:
      • Left Arrow: moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab. Optionally, activates the newly focused tab (See note below).
      • Right Arrow: Moves focus to the next tab. If focus is on the last tab element, moves focus to the first tab. Optionally, activates the newly focused tab (See note below).
    • When focus is on a tab in a tablist with either horizontal or vertical orientation:
      • Space or Enter: Activates the tab if it was not activated automatically on focus.
      • Home (Optional): Moves focus to the first tab
      • End (Optional): Moves focus to the last tab.
      • Shift + F10: If the tab has an associated pop-up menu, opens the menu.
      • Delete (Optional): If deletion is allowed, deletes (closes) the current tab element and its associated tab panel. If any tabs remain, sets focus to the tab following the tab that was closed and activates the newly focused tab. Alternatively, or in addition, the delete function is available in a context menu.

    Note:

    1. It is recommended that tabs activate automatically when they receive focus as long as their associated tab panels are displayed without noticeable latency. This typically requires tab panel content to be preloaded. Otherwise, automatic activation slows focus movement, which significantly hampers users’ ability to navigate efficiently across the tab list. For additional guidance, see 5.4 Deciding When to Make Selection Automatically Follow Focus.
    2. If the tabs in a tab list are arranged vertically:
      1. Down Arrow performs as Right Arrow is described above.
      2. Up Arrow performs as Left Arrow is described above.
    3. If the tab list is horizontal, it does not listen for Down Arrow or Up Arrow so those keys can provide their normal browser scrolling functions even when focus is inside the tab list.

    As usual, the tab panel needs to be keyboard operable to be accessible to screen readers. The onKeyDown() function is added to the functions, to add arrow key navigation between tabs, and between tabs and panels. Tab navigation and Enter keys are enabled by default and do not need to be defined here.

    A link to an interactive elements can be found at the bottom of this page.

    The onKeyDown function is then added to each tab, referenced with jQuery’s .on('keydown') function, added to the init() function’s $tab definition.

    Now, with keyboard access and WAI-ARIA added to define the semantics of the tab panel, it should be fully functional for screen readers.

    A link to an interactive elements can be found at the bottom of this page.

    Accessible Tab Panel in Action

    Watch the following video showing ChromeVox interacting with a tab panel. The Tab key is used to navigate into the tab panel and to the first tab. The arrow keys are used to move between tabs and, when on a tab, the Tab key is used to navigate to the associated panel. While on a panel, Shift + Tab is used to return to the tablist. There might also be Up and Down arrows enabled to move between tabs and panels, though we have not enabled them here. Aim to have the tab panel you update in the activity coming up on the next page operate and announce itself like the one in the video.

    Video: Accessible Tab Panel

    Thumbnail for the embedded element "Accessible Tab Panel"

    A YouTube element has been excluded from this version of the text. You can view it online here: https://pressbooks.library.ryerson.ca/wafd/?p=357


    This page titled 6.6: Tab Panels is shared under a CC BY-SA license and was authored, remixed, and/or curated by Digital Education Strategies, The Chang School.

    • Was this article helpful?