這系列無障礙的鐵人賽文章,實踐的內容主要是根據 W3C:WAI-ARIA 的實踐,從設計模式及組件(Design Patterns and Widgets)裡面挑選最想嘗試的,如果有朋友想瞭解全部 Widget 該怎麼實作及其規範,歡迎自行爬規範內容,也許我們可以討論一下;若以下文章內容理解有任何錯誤,請多指教~
(圖片來源:unsplash)
3.1 Accordion
An accordion is a vertically stacked set of interactive headings that each contain a title, content snippet, or thumbnail representing a section of content. The headings function as controls that enable users to reveal or hide their associated sections of content. Accordions are commonly used to reduce the need to scroll when presenting multiple sections of content on a single page.
我們在網頁實務上也算蠻常使用手風琴這個組件的,通常用減少於頁面上多個區塊顯示內容過長的捲動,它是一組垂直堆疊、可操作的標題,每個標題的關聯內容可以自定為折疊、不折疊,裡面包含一個小標、內容或能呈現意象的縮圖。使用者透過與標題的互動,可以顯示或隱藏其關聯的內容。
提供給使用者用滑鼠點選、用鍵盤操作的標題區塊。
與各個標題區塊,各自相關的內容。
我的第一個手風琴是在 W3Schools 學習刻出來的!
以前都不知道 outline: none;
其實是不好的做法,我用得很爽啊啊啊。(大驚)
更多選擇性的按鍵操控,請閱讀文件。
button
的不用管它,但是非語義標籤需要新增 role="button"
。button
每個可摺疊標題按鈕都包裝在一個元素角色為 headings
的標籤,加上 aria-level="層級"
,層級的值要符合於頁面架構。
<h3>
,那麼已經含有 aria-level
的意義,就不用再設定 aria-level="3"
。headings
角色的元素裡,只能有一個 button
角色的元素。如果還有其他一定要呈現的視覺內容,就放在 headings
之外。headings > button
button
加上 aria-expanded="true"
aria-expanded="false"
。aria-controls
,值是 Accordion Panel 容器的 id
。button
去隱藏當前顯示的內容」,如果有這樣的需求,就在 button
上加上 aria-disabled="true"
。role="region"
,再加上 aria-labelledby
,值為 headings > button
的 id
。
region
對於螢幕閱讀器理解結構是很有幫助的。region
用在你同時展開六個以上區塊內容的時候,這樣很混肴。換句話說,要用的話,你可以做一個六個區塊的手風琴,但一次只顯示一個區塊內容。希望能做到以下兩點:
ARIA
屬性。
<summary>
、 <details>
,因為支援度低, IE 6~11、Edge 較多人使用的版本不支援,所以還是採取以下做法。<div>
包裹,這裡直接使用 <button>
,保持鍵盤可操作、螢幕閱讀器可念出包裹的標題名稱與原生語義 Role
,樣式什麼的就交給 CSS 吧。 <div class="accordion">
<button class="accordion__header" aria-controls="panel-1">
<h3 class="accordion__header__title">
關於我
</h3>
</button>
<div class="accordion__panel" id="panel-1" role="region">
<p class="accordion__panel__content">我是 Askie Lin,目前鑽研前端領域中。我喜歡記錄我學習過的事物,也因此有可能出現錯誤的地方,如有文章不足之處,歡迎大家在我的文章底下留言告訴我唷,謝謝!</p>
</div>
<button class="accordion__header" aria-controls="panel-2">
<h3 class="accordion__header__title">
一起實踐無障礙吧
</h3>
</button>
<div class="accordion__panel" id="panel-2">
<p class="accordion__panel__content">還是無障礙的新手,等級一啊.....</p>
</div>
</div>
.accordion {
box-sizing: border-box;
margin: 10px;
max-width: 320px;
&__header {
display: block;
position: relative;
background: #ecf3f2;
border: 0;
width: 100%;
text-align: left;
padding: 0.5rem 15px;
border: 1px solid #999;
color: #333;
&:first-child {
border-bottom: 0;
}
&:hover {
cursor: pointer;
}
&:after {
content: '';
border: .4em solid transparent;
border-left: .5em solid #222;
bottom: 0;
height: 0;
margin: auto;
position: absolute;
right: 1rem;
top: 0;
transition: transform .2s ease-in-out;
transform-origin: center center;
transform: rotate(0deg);
width: 0;
}
&__title {
font-size: 1.5rem;
}
&.active {
background: #00a988;
color: #fff;
border: 1px solid #00a988;
&:after {
border-left-color: #fff;
transform: rotate(90deg);
right: 1.25rem;
top: 5px;
}
+ .accordion__panel {
display: block;
}
}
}
&__panel {
padding: 0 15px;
border-left: 2px solid #ecf3f2;
border-right: 2px solid #ecf3f2;
max-height: 0;
transition: max-height .2s ease-in-out, padding .2s ease-in-out;
display: none;
&[aria-hidden="false"] {
max-height: 100vh;
padding: 1em 15px 1.5em;
display: block;
}
&:last-child {
border-bottom: 2px solid #ecf3f2;
}
&__content {
font-size: 1.15rem;
line-height: 1.5;
color: #333;
}
}
}
const accordionButtons = document.querySelectorAll('.accordion__header');
const defaultActiveButton = document.querySelector('.accordion__header.active');
const openOneAccordion = (header) => {
accordionButtons.forEach(button => {
button.classList.remove('active');
button.removeAttribute('role');
button.setAttribute('aria-expanded', false);
button.nextElementSibling.setAttribute('aria-hidden', true);
});
header.classList.add('active');
header.setAttribute('role', 'region');
header.setAttribute('aria-expanded', true);
header.nextElementSibling.setAttribute('aria-hidden', false);
}
/* 初始化預設開啟的按鈕 */
if(defaultActiveButton){
openOneAccordion(defaultActiveButton);
}
/* 監聽每個標題 */
accordionButtons.forEach(button => {
button.addEventListener('click', () => {
openOneAccordion(button);
})
});
可以打開 Codepen 看動起來的樣子,用鍵盤操作看看。