Skip to content

Instantly share code, notes, and snippets.

@H-ymt
Last active November 23, 2024 10:57
Show Gist options
  • Save H-ymt/2dfca6f25b89ff506fabb753d51bc841 to your computer and use it in GitHub Desktop.
Save H-ymt/2dfca6f25b89ff506fabb753d51bc841 to your computer and use it in GitHub Desktop.
アクセシビリティに配慮したドロップダウンメニュー
export default function initializeDropdown() {
const dropdownTriggers = document.querySelectorAll(".js-dropdownTrigger")
const dropdownLists = document.querySelectorAll(".js-dropdownList")
dropdownTriggers.forEach((trigger, index) => {
const dropdownList = dropdownLists[index]
if (!trigger || !dropdownList) return
let isExpanded = false
let currentIndex = 0
const dropdownItems = Array.from(dropdownList.querySelectorAll(".js-dropdownListItem"))
const toggleAriaExpanded = (expanded) => {
trigger.setAttribute("aria-expanded", expanded)
isExpanded = expanded
if (expanded && dropdownItems.length > 0) {
dropdownItems[currentIndex].firstElementChild.focus()
}
}
const handleFocusOut = (event) => {
if (
!dropdownList.contains(event.relatedTarget) &&
!trigger.contains(event.relatedTarget)
) {
toggleAriaExpanded(false)
}
}
const handleOutsideClick = (event) => {
if (!dropdownList.contains(event.target) && !trigger.contains(event.target)) {
toggleAriaExpanded(false)
}
}
const handleEscapeKey = (event) => {
if (event.key === "Escape") {
toggleAriaExpanded(false)
}
}
const handleKeyDown = (event) => {
const { key } = event
if (key === "ArrowDown" || key === "ArrowUp") {
event.preventDefault()
if (key === "ArrowDown") {
currentIndex = (currentIndex + 1) % dropdownItems.length
} else if (key === "ArrowUp") {
currentIndex = (currentIndex - 1 + dropdownItems.length) % dropdownItems.length
}
dropdownItems[currentIndex].firstElementChild.focus()
}
}
const toggleDropdown = () => toggleAriaExpanded(!isExpanded)
trigger.addEventListener("click", toggleDropdown)
dropdownList.addEventListener("focusout", handleFocusOut)
document.addEventListener("click", handleOutsideClick)
document.addEventListener("keydown", handleEscapeKey)
dropdownList.addEventListener("keydown", handleKeyDown)
if (dropdownItems.length > 0) {
dropdownItems[currentIndex].firstElementChild.focus()
}
})
}
<nav class="headerNav" aria-label="グローバルナビゲーション">
<a href="/" class="headerNav__link">Home</a>
<div class="headerNav__dropdownWrapper">
<button
type="button"
aria-controls="headerDropdown"
aria-label="サービスメニュー"
class="headerNav__link headerNav__dropdown js-dropdownTrigger"
>
Services
</button>
<ul id="headerDropdown" class="headerDropdown js-dropdownList">
<li role="none" class="headerDropdown__item js-dropdownListItem">
<a class="headerDropdown__link" href="/websites">Websites</a>
</li>
<li role="none" class="headerDropdown__item js-dropdownListItem">
<a class="headerDropdown__link" href="/ecommerce">UI/UX</a>
</li>
<li role="none" class="headerDropdown__item js-dropdownListItem">
<a class="headerDropdown__link" href="/ecommerce">eCommerce</a>
</li>
<li role="none" class="headerDropdown__item js-dropdownListItem">
<a class="headerDropdown__link" href="/mobile-app"
>Mobile App</a
>
</li>
</ul>
</div>
<a href="headerNav__link" class="headerNav__link">About</a>
</nav>
@H-ymt
Copy link
Author

H-ymt commented Apr 25, 2024

  • エンターおよびスペースキーでドロップダウンメニューが表示される。
  • ドロップダウンメニューの表示/非表示にあわせ、aria-expanded属性の値が切り替わる。
  • キーボードでフォーカスおよびフォーカスアウトした際も表示・非表示が切り替わる。
  • escキー押下でドロップダウンメニューが閉じる。
  • ドロップダウンメニュー内を矢印キーで移動できる。
  • ドロップダウンメニューが表示されている時、ドロップダウンメニュー以外をクリックするとメニューが閉じる。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment