Enter The Dragon (Drop):可訪問列表重新排序
已發表: 2022-03-10作為一名專注於可訪問性的 Web 開發人員,多年來,我主要處理廣泛採用的標準化 UI 組件,這些組件得到了輔助技術 (AT) 的良好支持。 對於這些類型的小部件,有簡潔的 ARIA 創作實踐以及諸如 axe-core 之類的出色工具,可用於測試 Web 組件的可訪問性問題。 創建不太常見的小部件,尤其是那些沒有廣泛採用的用戶交互約定的小部件可能非常棘手。
我遇到的最艱鉅的挑戰之一是可重新排序的拖放列表。 雖然可重新排序的列表是一種比較常用的小部件,對鼠標用戶來說具有直觀的約定,但尚不清楚僅使用鍵盤的輔助技術用戶如何執行這個簡單的任務。 由於缺乏支持的 ARIA 屬性,Dragon Drop 使用實時區域來傳達所有用戶重新排序列表所需的信息。
實時區域
活動區域是配備 ARIA 屬性的 HTML 元素,可用於通知屏幕閱讀器內容更改。 它們允許我們提供完全定制的屏幕閱讀器公告,而無需移動焦點! 實時區域非常適合頁面遠程部分的實時更新、狀態更新和時間敏感信息。
它們通常用於聊天日誌、進度指示器、體育比分更新和天氣警報,但應謹慎使用,因為它們很容易為屏幕閱讀器用戶創建過於冗長的體驗。 如果您是 live region 的新手,或者只是想探索他們可以做什麼,請查看我的 live region playground,它允許您配置自己的自定義 live region。
如果您想在應用程序中使用實時區域,請參閱 npm 上的實時區域模塊。
<div aria-live="assertive" role="log" aria-relevant="additions" aria-atomic="true"></div>
為什麼使用實時區域?
在理想情況下,我可以簡單地依賴拖放式 ARIA 屬性aria-grabbed
和aria-dropeffect
。 然而,實際上,對這些屬性的支持很少見,而且在支持的情況下,對於屏幕閱讀器用戶來說,體驗是令人困惑和違反直覺的。 最重要的是,這兩個屬性自 ARIA 1.1 以來已被棄用,這意味著我們將來不會看到對這些屬性的支持增加。
可以在此處找到有關此棄用的 W3C 對話。 由於這些問題,我決定不在 Dragon Drop 中使用aria-grabbed
和aria-dropeffect
。 在輔助技術/瀏覽器配對中,對 ARIA 屬性的不同支持在可訪問性世界中非常普遍。 幸運的是,諸如role
、 aria-live
、 aria-relevant
和aria-atomic
等實時區域屬性得到了 JAWS、NVDA 和 VoiceOver 等屏幕閱讀器的廣泛支持。
優化的可訪問性
Dragon Drop 是一個高度可配置的列表重新排序模塊,適用於鼠標、鍵盤和輔助技術用戶。 幾年前,當我決定編寫它時,在我一直從事的項目中多次提出了可訪問的重新排序列表,但我還沒有看到一個可行的解決方案。 在我遇到的幾十個拖放列表重新排序插件中,大多數在設計時都沒有考慮到可訪問性,因此非常難以訪問。
Dragon Drop 已經過 VoiceOver、JAWS 和 NVDA 測試,使 AT 用戶能夠成功地重新排序列表。
我著手回答任何用戶在遇到可重新排序的列表時可能遇到的所有問題。 這些問題已經通過常見的約定為有視力的鼠標用戶提供了答案,但其他用戶呢?
我如何取貨?
通過提供一個傳達項目抓取狀態的控件以及一些頂級幫助文本,我們可以回答這個問題。 例如,一個帶有可訪問文本的控件(由 AT 收集,雖然它的名稱、角色和值)“重新排序項目 1,切換按鈕”告訴用戶它是一個按鈕,當激活時,它將拾取項目並啟動一個重新排序。
Dragon drop 使用aria-pressed
屬性讓 AT 用戶知道一個項目什麼時候被“拖動”,什麼時候沒有。 它可以被配置為允許整個項目是可拖動的,或者只是一個子“拖動手柄”,在任何一種情況下都可以確保role="button"
和tabindex="0"
的存在。
當拖動項目被激活時, aria-pressed="true"
被應用到元素並且一個可配置的公告,例如“項目 1 已被抓取”通過活動區域被讀出給屏幕閱讀器。
如何移動項目?
利用aria-describedby
將控件與有用的幫助文本相關聯,例如“激活重新排序按鈕並使用箭頭鍵重新排序列表或使用鼠標拖動/重新排序”。 告訴用戶如何進行實際重新排序。 這讓屏幕閱讀器用戶知道,當按下某個項目時,向上和向下箭頭鍵將分別在列表中上下移動該項目。
如何丟棄物品?
因為使用了aria-pressed
屬性,所以用戶可以很容易地知道如何放置一個項目。 以某種方式、形狀或形式,所有最廣泛使用的屏幕閱讀器都傳達了切換按鈕的按下狀態。 aria-pressed 屬性設置為“false”,當一個項目被刪除並且自定義公告(如“項目 1 已刪除” )被朗讀給屏幕閱讀器時。
我如何知道列表何時重新排序?
每次使用向上和向下箭頭鍵更改列表的順序時,我們都需要確保通知所有用戶此更改。 對於盲人用戶,我們必須再次依賴直播區域。 可配置的公告,例如“列表已重新排序,第 1 項現在在列表中排在第 4 位。” , 被讀出以傳達重新排序列表的更新狀態。 這很重要,因為視力正常的用戶可以立即看到更改順序的視覺反饋,並且需要將相同的信息傳達給 AT 用戶。
如何取消重新訂購?
由於我們不能依賴廣泛採用的約定來進行此類交互,因此我們可以簡單地在幫助文本中包含諸如“按轉義鍵取消重新排序”之類的指示。 此外,我們利用實時區域提供定制的讀數,通知用戶取消。
鍵盤交互
鑰匙 | 行為 |
---|---|
輸入或空格 | 在“抓取”和“丟棄”狀態之間切換項目 |
“↓” | 在列表中向下移動“抓取的”項目 |
“↑” | 在列表中向上移動“抓取”的項目 |
退出 | 取消重新排序並恢復初始訂單 |
看龍在行動
查看 Dragon Drop 演示,您可以在其中體驗幾種不同的配置。
將 Dragon Drop 放入您的應用程序
Dragon Drop 將您的普通列表轉換為完全可訪問的拖放可重新排序列表:
<ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> <script> const dragon = document.getElementById('dragon'); // Enter the dragon new DragonDrop(dragon); </script>
安裝
Dragon Drop 是一個開源(MIT 許可)項目,可以通過 npm 安裝:
$ npm install drag-on-drop
它可以與 browserify 或 webpack 等模塊一起使用:
// if you're not down with ES6, you can require('drag-on-drop') import DragonDrop from 'drag-on-drop';
使用 unpkg CDN 也可以輕鬆地將 Dragon Drop 拖放到您的頁面中:
<script source="https://unpkg.com/drag-on-drop"></script> <script> var dragonDrop = new DragonDrop(listElement); </script>
配置
為了支持廣泛的用例,Dragon Drop 是非常可配置的。
下面是默認配置:
{ item: 'li', handle: 'button', activeClass: 'dragon-active', inactiveClass: 'dragon-inactive', announcement: { grabbed: el => `Item ${el.innerText} grabbed`, dropped: el => `Item ${el.innerText} dropped`, reorder: (el, items) => { const pos = items.indexOf(el) + 1; const text = el.innerText; return `The list has been reordered, ${text} is now item ${pos} of ${items.length}`; }, cancel: 'Reordering cancelled' } }
公告
Dragon Drop 的“公告”配置選項是最重要的,因為它是 Dragon Drop 提供的屏幕閱讀器體驗的支柱。 它是一個包含"grabbed"
、 "dropped"
、 "reorder"
和"cancel"
函數的對象,允許為發生的所有交互自定義實時區域公告。
這些函數中的每一個都必須返回一個公告文本字符串,當給定操作發生時,該字符串將添加到活動區域。 利用這些功能的另一個好處是它支持本地化實時區域消息。
為了便於通知,發生動作的列表項元素和列表中的項數組分別作為參數傳遞。
{ announcement: { // grabbed is called when an item is picked up grabbed: (targetItem, items) => `${targetItem.innerText} grabbed`, // dropped is called when an item is dropped dropped: (targetItem, items) => `${targetItem.innerText} grabbed`, // reorder is called each time the order of the list is altered reorder: (targetItem, items) => { return `${targetItem.innerText} is now ${items.indexOf(targetItem) + 1} of ${items.length}` }, // cancel is called when a reordering is cancelled (via escape key) cancel: () => 'The initial order has been restored, reordering cancelled' } }
幫助文本
提供描述如何使用可重新排序列表的幫助文本是絕對重要的。 這是 Dragon Drop 不會為您做的事情,以便在如何將此文本提供給輔助技術方面保持不那麼固執己見。 推薦的實現是使用aria-describedby
將幫助文本與交互式項目相關聯,如下所示:
<p>Activate the reorder button and use the arrow keys to reorder the list or use your mouse to drag/reorder. Press escape to cancel the reordering.</p> <ul> <li> <button>Reorder Item 1</button> <span>Item 1</span> </li> <li> <button>Reorder Item 2</button> <span>Item 2</span> </li> <li> <button>Reorder Item 3</button> <span>Item 3</span> </li> </ul>
GitHub上的龍滴
Dragon Drop的第三個版本最近已經發布。 如果您有興趣使用它,請參閱 GitHub 上的 Dragon Drop 文檔。 特別感謝 Dragula 的創建者,Dragon Drop 用於鼠標交互的模塊,以及設計了令人敬畏的標誌的 Aaron Pearlman!
龍的未來
如果未來在 WAI-ARIA 技術規範中加入拖放交互,Dragon Drop 依賴非標準交互和直播區域的情況可能會發生變化。 我將繼續進行測試,確保它得到盡可能多的屏幕閱讀器的良好支持,並與最新的 ARIA 規範保持同步。 此外,還有很多功能正在開發中,包括支持觸摸屏/移動設備以及多列列表(如衝刺板)。 未來可能添加的另一個功能是 Dragon Drop React 組件。
目前,Dragon Drop 可以與 React 一起使用,如下面的演示所示,但它並不理想,因為 React 不會拾取由列表重新排序引起的 DOM 更改,這可能會導致意外行為。 我敦促任何在 Dragon Drop 中發現錯誤,甚至對功能有想法的人在 GitHub 上創建問題。 歡迎並非常感謝所有反饋和貢獻!