Last active
September 22, 2021 07:36
-
-
Save manabuyasuda/3799c3e5f7e4564532740e75b0d1afea to your computer and use it in GitHub Desktop.
Accordion(ES2015)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* アコーディオンコンポーネントです。 | |
* @author Manabu Yasuda | |
* @see https://www.w3.org/TR/wai-aria-practices-1.1/examples/accordion/accordion.html | |
* @param {CSS selectors} root ['.js-accordion'] - ルートに指定するクラス名です。 | |
* @param {CSS selectors} tabs ['.js-accordion-tab'] - トリガーに指定するクラス名です。`button`タグを推奨します。 | |
* @param {CSS selectors} tabpanels ['.js-accordion-panel'] - コンテンツに指定するクラス名です。 | |
* @param {CSS selectors} initializedClass ['.js-accordion-initialized'] - 初期化完了時に付与するクラス名です。 | |
* @param {boolean} useRole [false] - `role`属性が付与されます。 | |
* @param {boolean} firstChildShow [true] - ルートの最初の要素が開きます。 | |
* @param {boolean} multiselectable [true] - 複数の要素を同時に開きます。 | |
* @param {String} tabsPrefix ['accordion-'] - `tabs`に付与するid属性のプレフィックスです。 | |
* @param {String} tabpanelsPrefix ['accordion-panel-'] - `tabpabels`に付与するid属性のプレフィックスです。 | |
*/ | |
export default class Accordion { | |
constructor(options) { | |
const defaultOptions = { | |
root: '.js-accordion', | |
tabs: '.js-accordion-tab', | |
tabpanels: '.js-accordion-panel', | |
initializedClass: 'js-accordion-initialized', | |
useRole: false, | |
firstChildShow: true, | |
multiselectable: true, | |
tabsPrefix: 'accordion-', | |
tabpanelsPrefix: 'accordion-panel-', | |
} | |
this.options = Object.assign(defaultOptions, options) | |
Object.keys(this.options).forEach((key) => { | |
this[key] = this.options[key] | |
}) | |
this.selector = { | |
root: Array.from(document.querySelectorAll(this.root)), | |
tabs: Array.from(document.querySelectorAll(this.tabs)), | |
tabpanels: Array.from(document.querySelectorAll(this.tabpanels)), | |
} | |
} | |
init() { | |
if (!this.selector.root.length) return | |
this.setAttributes() | |
if (this.useRole) { | |
this.setRole() | |
} | |
this.selector.root[0].classList.add(this.initializedClass) | |
if (this.firstChildShow) { | |
this.showFirstChild() | |
} | |
this.toggle() | |
} | |
setAttributes() { | |
this.selector.tabs.forEach((tab, index) => { | |
tab.setAttribute('id', this.tabsPrefix + (index + 1)) | |
tab.setAttribute('aria-controls', this.tabpanelsPrefix + (index + 1)) | |
tab.setAttribute('aria-expanded', false) | |
}) | |
this.selector.tabpanels.forEach((tabpanel, index) => { | |
tabpanel.setAttribute('id', this.tabpanelsPrefix + (index + 1)) | |
tabpanel.setAttribute('aria-labelledby', this.tabsPrefix + (index + 1)) | |
tabpanel.setAttribute('aria-hidden', true) | |
}) | |
} | |
setRole() { | |
this.selector.root.forEach((item) => { | |
item.setAttribute('role', 'tablist') | |
}) | |
this.selector.tabs.forEach((tab) => { | |
tab.setAttribute('role', 'tab') | |
}) | |
this.selector.tabpanels.forEach((tabpanel) => { | |
tabpanel.setAttribute('role', 'tabpanel') | |
}) | |
} | |
showFirstChild() { | |
this.selector.tabs[0].setAttribute('aria-expanded', true) | |
this.selector.tabpanels[0].setAttribute('aria-hidden', false) | |
} | |
toggle() { | |
this.selector.tabs.forEach((tab) => { | |
tab.addEventListener('click', (e) => { | |
const tabID = document.getElementById(e.currentTarget.id) | |
const isExpanded = tabID.getAttribute('aria-expanded') !== 'false' | |
if (!this.multiselectable && !isExpanded) { | |
this.hideAll() | |
} | |
tabID.setAttribute('aria-expanded', !isExpanded) | |
document | |
.querySelector(`[aria-labelledby="${e.currentTarget.id}"]`) | |
.setAttribute('aria-hidden', isExpanded) | |
e.stopPropagation() | |
e.preventDefault() | |
}) | |
}) | |
} | |
hideAll() { | |
this.selector.tabs.forEach((hideTab) => { | |
const hideTabID = hideTab.getAttribute('id') | |
hideTab.setAttribute('aria-expanded', false) | |
document.querySelector(`[aria-labelledby="${hideTabID}"]`).setAttribute('aria-hidden', true) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment