MENU

タブ機能の作り方【アクセシブルな実装シリーズ】

techlab / baigie
タブ機能の作り方【アクセシブルな実装シリーズ】 | techlab / baigie 株式会社ベイジのエンジニアチームがお届けする技術情報、チームの取り組みの紹介、お役立ち小話等に関する情報発信メディアです。
	<div class="tab-area">
		<h2 id="tablist-label">タブ見本項目一覧</h2>
		<div class="tab-list" role="tablist" aria-labelledby="tablist-label">
			<button class="tab -active js-tab" type="button" role="tab" id="tab1" aria-controls="tab-panel1"
				aria-selected="true">タブ1</button>
			<button class="tab js-tab" type="button" role="tab" id="tab2" aria-controls="tab-panel2" aria-selected="false"
				tabindex="-1">タブ2</button>
			<button class="tab js-tab" type="button" role="tab" id="tab3" aria-controls="tab-panel3" aria-selected="false"
				tabindex="-1">タブ3</button>
		</div>
		<div id="tab-panel1" class="tab-panel js-tab-panel -active" role="tabpanel" tabindex="0" aria-labelledby="tab1">
			<p>
				タブパネル1の内容
			</p>
		</div>
		<div id="tab-panel2" class="tab-panel js-tab-panel" role="tabpanel" tabindex="0" aria-labelledby="tab2">
			<p>
				タブパネル2の内容
			</p>
		</div>
		<div id="tab-panel3" class="tab-panel js-tab-panel" role="tabpanel" tabindex="0" aria-labelledby="tab3">
			<p>
				タブパネル3の内容
			</p>
		</div>
	</div>
.tab-area {
  max-width: 600px;
  margin-right: auto;
  margin-left: auto;
}

.tab-list {
  display: flex;
  column-gap: 2px;
}

.tab {
  cursor: pointer;
  flex: 1;
  padding-top: 10px;
  padding-bottom: 10px;
  border: 1px solid #666;
}
.tab.-active {
  background-color: #fff;
}

.tab-panel {
  padding: 20px;
  display: none;
  border: 1px solid #666;
  line-height: 1.75;
}
.tab-panel.-active {
  display: block;
}
const tabs = document.querySelectorAll('.js-tab')
function tabSwitch(){
  let tabsArray = Array.prototype.slice.call(tabs);
  let index = tabsArray.indexOf(this);
  const resetTab = function(){
    document.querySelector('.js-tab.-active').classList.remove('-active');
    document.querySelector('.js-tab[aria-selected=true]').removeAttribute('aria-selected');
    document.querySelectorAll('.js-tab').forEach((elm)=>{
      elm.tabIndex = -1;
    });
    document.querySelector('.js-tab-panel.-active').classList.remove('-active');
  }
  const setTab = function(tab,tabpanel) {
    tab.classList.add('-active');
    tab.tabIndex = 0;
    tab.setAttribute('aria-selected',true);
    tabpanel.classList.add('-active');
  }
  if (event.type == 'keyup') {
    if(event.key === 'ArrowRight') {
      if(tabsArray[index + 1]) {
        tabsArray[index + 1].focus();
        resetTab();
        setTab(tabsArray[index + 1],document.querySelectorAll('.js-tab-panel')[index + 1]);
        } else {
         tabsArray[0].focus();
         resetTab();
         setTab(tabsArray[0], document.querySelectorAll('.js-tab-panel')[0]);
        };
      };
    if(event.key === 'ArrowLeft') {
      if(tabsArray[index - 1]) {
         tabsArray[index - 1].focus();
         resetTab();
         setTab(tabsArray[index - 1], document.querySelectorAll('.js-tab-panel')[index - 1])
        } else {
         let lastTab =  tabsArray.pop();
         lastTab.focus();
         resetTab();
         setTab(lastTab, Array.prototype.slice.call(document.querySelectorAll('.js-tab-panel')).pop());
        };
      };
  }
	if (event.type == 'click') {
    resetTab();
    setTab(this, document.querySelectorAll('.js-tab-panel')[index]);
  }
};

tabs.forEach((tab)=>{
  tab.addEventListener('click',tabSwitch);
  tab.addEventListener('keyup',tabSwitch);
});
目次