Roving Tabindex - Tab Pane example

by tohuwabohu_io

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Roving Tabindex - Tab Pane example</title>
</head>
<body>
<main>
    <h1>Tab Pane</h1>
    <div class="tab-pane">
        <div>
            <ul class="tabbed" role="tablist">
                <li role="tab">
                    <button onclick="selectTab(this, 1)" class="active-tab" tabindex="0">Tab 1</button>
                </li>
                <li role="tab">
                    <button onclick="selectTab(this, 2)" tabindex="-1">Tab 2</button>
                </li>
                <li role="tab">
                    <button onclick="selectTab(this, 3)" tabindex="-1">Tab 3</button>
                </li>
                <li role="tab">
                    <button onclick="selectTab(this, 4)" tabindex="-1">Tab 4</button>
                </li>
            </ul>
        </div>

        <div class="panes">
            <div id="pane1">
                <h2>
                    Content 1
                </h2>
                <form>
                    <label for="name1">Name 1:</label>
                    <input id="name1" type="text">
                </form>
            </div>

            <div id="pane2" style="display: none" aria-hidden="true">
                <h2>
                    Content 2
                </h2>
                <form>
                    <label for="name2">Name 2:</label>
                    <input id="name2" type="text">
                </form>
            </div>

            <div id="pane3" style="display: none" aria-hidden="true">
                <h2>
                    Content 3
                </h2>
                <form>
                    <label for="name3">Name 3:</label>
                    <input id="name3" type="text">
                </form>
            </div>

            <div id="pane4" style="display: none" aria-hidden="true">
                <h2>
                    Content 4
                </h2>
                <form>
                    <label...

CSS

.tab-pane {
  display: flex;
  flex-direction: column;
}

.tabbed > li {
  float: left;
  list-style: none;
  width: 100px;
  height: 50px;
  margin-right: 10px;
}

.tabbed > li > button {
  width: 100%;
  height: 100%;
  font-size: 14pt;
  background-color: white;
}

.active-tab {
  border-bottom: 0;
  background-color: aliceblue !important;
}

.panes {
  background-color: aliceblue;
  padding-bottom: 1em;
}

JavaScript

const buttons = Array
	.from(document.querySelectorAll("ul[role='tablist'] > li > button"));

buttons.forEach(button => {
  button.addEventListener('keydown', (e) => {
    console.log(e.key);

    if (e.key !== 'Tab' && e.key !== 'Enter') {
      e.preventDefault();
    }

    const tabs = Array
    .from(document.querySelectorAll("ul[role='tablist'] > li > button"));

    const thisIndex = tabs.indexOf(e.target);

    switch (e.key) {
      case 'ArrowLeft': {
        roveTabindex(tabs, thisIndex, -1);
      }
        break;
      case 'ArrowRight': {
        roveTabindex(tabs, thisIndex, 1);
      }
        break;
      default:
        return;
    }
  });
});

const roveTabindex = (tabs, index, inc) => {
  let nextIndex = index + inc;
  if (nextIndex < 0) {

    nextIndex = tabs.length - 1;
  } else if (nextIndex >= tabs.length) {
    nextIndex = 0;
  }
  console.log(`roving tabindex to index ${nextIndex}`);

  tabs[index].setAttribute("tabindex", "-1");

  tabs[nextIndex].setAttribute("tabindex", "0");
  tabs[nextIndex].focus();
}

let selected = 1;

const selectTab = (tab, id) => {
  const pane = document.querySelector(`div[id=pane${selected}]`);
  const next = document.querySelector(`div[id=pane${id}]`);
  const activeTab = document.querySelector('button[class=active-tab]');

  pane.setAttribute("style", "display: none");
  pane.setAttribute("aria-hidden", "true");

  next.setAttribute("style", "display: block");
  next.setAttribute("aria-hidden", "false");

  activeTab.classList.remove('active-tab');
  tab.classList.add('active-tab');

  selected = id;
}