1 回答

TA貢獻(xiàn)1797條經(jīng)驗 獲得超6個贊
感謝您回答這些問題,希望以下解釋能夠突出我問他們的原因(然后我將提供一些解決方案)。
為什么我們不能直接攔截tab密鑰呢?
屏幕閱讀器用戶不能僅使用 Tab 鍵進(jìn)行導(dǎo)航。根據(jù)他們使用的屏幕閱讀器,他們使用不同的快捷方式通過標(biāo)題、鏈接、表單等進(jìn)行導(dǎo)航。
這會導(dǎo)致彈出窗口的可訪問性問題,因為人們只傾向于捕獲密鑰tab。然后,如果用戶使用快捷方式(例如2在 NVDA 中)跳轉(zhuǎn)頁面上的第 2 級標(biāo)題,他們最終可能會在不知道模態(tài)存在的情況下跳到模態(tài)之外,而且通常沒有任何方法可以在不進(jìn)行 Tab 切換很長時間的情況下返回到模態(tài)。
所以解決方案很明顯,確保頁面上的其他內(nèi)容都不可訪問(而不僅僅是不可聚焦)。
然而,您需要對 DOM 結(jié)構(gòu)進(jìn)行良好的排序/組織,以使其易于管理。
需要解決的問題
屏幕閱讀器用戶可以訪問不可聚焦的元素
他們可以更改快捷鍵,這樣我們就不能依靠攔截按鍵來嘗試解決問題。
我們希望保持相同的視覺設(shè)計(即我們不能只
display:none
在所有其他元素上使用)。我們想要一種可以重復(fù)的模式,這樣我們就不能單獨(dú)隱藏頁面上的元素。
我們希望正確管理焦點(diǎn),以便當(dāng)模式關(guān)閉時它將焦點(diǎn)恢復(fù)到上一個項目(在您的情況下)。
我們希望在到達(dá)最后一項時循環(huán)回到模式中的第一項(我們可以攔截鍵,tab因為我們無法覆蓋所有場景,我們也不希望這樣做,因為這會導(dǎo)致更多的可訪問性問題。)
解決方案
問題 1、2、3 和 4
由于我們無法攔截按鍵來管理模態(tài)中的焦點(diǎn),因此我們必須在模態(tài)處于活動狀態(tài)時使所有其他元素(模態(tài)中的元素除外)完全不可訪問。
aria-hidden="true"
display: none
對于屏幕閱讀器來說非常有效。所有屏幕閱讀器/瀏覽器組合的支持率aria-hidden
約為 90% 至 95%。
為了使模態(tài)之外的內(nèi)容無法訪問,我們需要添加aria-hidden="true"
到模態(tài)之外的每個元素,并tabindex="-1"
確保使用tab鍵無法將任何內(nèi)容聚焦到模態(tài)之外。
我詢問了您的文檔結(jié)構(gòu),因為實(shí)現(xiàn)這一點(diǎn)的最簡單方法是在區(qū)域/主要地標(biāo)上。
因此,當(dāng)模態(tài)處于活動狀態(tài)時,我們需要將aria-hidden="true"
和tabindex="-1"
添加到<head>
、等<main>
。<footer>
通過在地標(biāo)級別執(zhí)行此操作并將模態(tài)放在主文檔流之外,這將變得易于管理和維護(hù),同時保留語義 HTML 標(biāo)記。模態(tài)框的情況正好相反(因此當(dāng)它不活動時使用相同的技術(shù)隱藏它。)
模態(tài)打開前
<head aria-hidden="false"></head>
<main aria-hidden="false"></main>
<footer aria-hidden="false"></footer>
<div class="modal" aria-hidden="true" tabindex="-1"></div>
模態(tài)打開
<head aria-hidden="true" tabindex="-1"></head>
<main aria-hidden="true" tabindex="-1"></main>
<footer aria-hidden="true" tabindex="-1"></footer>
<div class="modal" aria-hidden="false"></div>
請注意我aria-hidden總是如何添加,因為某些屏幕閱讀器對動態(tài)添加反應(yīng)不佳a(bǔ)ria(盡管它們對更改屬性反應(yīng)良好)。
第 5 點(diǎn)和第 6 點(diǎn)
為此,我認(rèn)為最簡單的方法是分享我用來在模態(tài)中捕獲焦點(diǎn)的代碼。
以下函數(shù)的目的是在模式打開時將其聚焦在模式中的第一個可聚焦項目,存儲對激活模式的元素的引用(因為我們希望在模式關(guān)閉時將用戶返回到那里)并管理焦點(diǎn)。
請注意,我使用一個微型庫來啟用 jQuery 樣式選擇器,因此您可能需要根據(jù)您的使用進(jìn)行調(diào)整。
管理焦點(diǎn)解釋和代碼
該item變量是在打開模式之前按下的引用按鈕(因此我們可以在關(guān)閉模式后將焦點(diǎn)返回到那里)。
該className變量是模態(tài)的類名,因此您可以針對不同的模態(tài)。
kluio.helpers只是我在整個網(wǎng)站上使用的函數(shù)數(shù)組,因此可以省略。
kluio.globalVars是一個全局變量數(shù)組,因此可以代替從函數(shù)返回結(jié)果。
我為每個部分添加了注釋來解釋它的作用。
當(dāng)模態(tài)打開時調(diào)用該setFocus函數(shù),傳遞按下激活它的元素和模態(tài)的元素className(更適合我們的用例,您可以使用 ID 代替)。
var kluio = {};
kluio.helpers = {};
kluio.globalVars = {};
kluio.helpers.setFocus = function (item, className) { //we pass in the button that activated the modal and the className of the modal, your modal must have a unique className for this to work.
className = className || "content"; //defaults to class 'content' in case of error ("content" being the class on the <main> element.)
kluio.globalVars.beforeOpen = item; //we store the button that was pressed before the modal opened in a global variable so we can return focus to it on modal close.
var focusableItems = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', '[tabindex="0"]']; //a list of items that should be focusable.
var findItems = [];
for (i = 0, len = focusableItems.length; i < len; i++) {
findItems.push('.' + className + " " + focusableItems[i]); //add every focusable item to an array.
}
var findString = findItems.join(", ");
kluio.globalVars.canFocus = Array.prototype.slice.call($('body').find(findString)); //please note we use a custom replacement for jQuery, pretty sure .find() behaves identically but just check it yourself.
if (kluio.globalVars.canFocus.length > 0) {
setTimeout(function () { //set timeout not needed most of the time, we have a modal that is off-screen and slides in, setting focus too early results in the page jumping so we added a delay, you may be able to omit this.
kluio.globalVars.canFocus[0].focus(); //***set the focus to the first focusable element within the modal
kluio.globalVars.lastItem = kluio.globalVars.canFocus[kluio.globalVars.canFocus.length - 1]; //we also store the last focusable item within the modal so we can keep focus within the modal.
}, 600);
}
}
然后,我們keydown使用以下函數(shù)攔截該事件來管理焦點(diǎn)。
document.onkeydown = function (evt) {
evt = evt || window.event;
if (evt.keyCode == 27) {
closeAllModals(); //a function that will close any open modal with the Escape key
}
if (kluio.globalVars.modalOpen && evt.keyCode == 9) { //global variable to check any modal is open and key is the tab key
if (evt.shiftKey) { //also pressing shift key
if (document.activeElement == kluio.globalVars.canFocus[0]) { //the current element is the same as the first focusable element
evt.preventDefault();
kluio.globalVars.lastItem.focus(); //we focus the last focusable element as we are reverse tabbing through the items.
}
} else {
if (document.activeElement == kluio.globalVars.lastItem) { //when tabbing forward we look for the last tabbable element
evt.preventDefault();
kluio.globalVars.canFocus[0].focus(); //move the focus to the first tabbable element.
}
}
}
};
最后,在您的 closeAllModals 函數(shù)版本中,您需要將焦點(diǎn)返回到引用元素/按鈕。
if (kluio.globalVars.beforeOpen) {
kluio.globalVars.beforeOpen.focus();
}
一旦激活該行,kluio.globalVars.canFocus[0].focus(); 就會調(diào)用該行將焦點(diǎn)設(shè)置到模式中的第一個可聚焦項目,您不需要在第一個元素打開時按 Tab 鍵,它應(yīng)該自動聚焦。
點(diǎn) 5 被線覆蓋,kluio.globalVars.beforeOpen = item;以設(shè)置對打開模式的項目的引用,并kluio.globalVars.beforeOpen.focus();在關(guān)閉函數(shù)內(nèi)將焦點(diǎn)返回到該項目。
第 6 點(diǎn)包含在document.onkeydown從 開始的函數(shù)中if (kluio.globalVars.modalOpen && evt.keyCode == 9) {。
我希望以上所有內(nèi)容都清楚,有任何問題盡管問,如果以后有時間我會把它變成一個小提琴。
- 1 回答
- 0 關(guān)注
- 119 瀏覽
添加回答
舉報