最近在做 Accessibility 相關的東西時候,又發現了一個坑,就是跟這個看似完美的tabindex=0
的有關。
其實究其原因,還是年輕的我不仔細看文檔導致的,但我覺得文檔沒有特別強調的但是確實很重要的東西就有可能是未來的坑,所以還是想分享一下給大家。
要實現的需求大概是這樣的
有一個選擇框是這個樣子的,我們叫它 可以搜索的 Listbox
,可以點擊打開裏面的List
選擇,也可以點擊輸入你想要的國家英文名稱,它會模糊進行 Filter
給出你想要的結果。
但我們今天想說的不是它的搜索和選擇功能,而是你看到的這個組件的無障礙可訪問功能(Web Accessibility)中的輸入空格的功能。
可能你還是不是太明白,再看個圖吧,我在按下了 Tab
鍵之後,這個 Listbox
會被聚焦,並且有藍色的邊框,這個就是用這行代碼實現的。
//僞代碼
<div tabindex="0" role="listbox">
<input tabindex="-1" />
</div>
在我添加了一系列點擊事件之後,我發現一旦它被Focus
之後,按下空格和回車鍵的時候,它就會依次打開、關閉,心中竊喜,簡直完美,也沒有仔細看原因.......
然後呢,再測試的時候就有問題了,當我在輸入框輸入一串字符,接着想輸入一個空格的時候,我的Listbox
直接就給我合上了,這是怎麼回事呢,什麼都沒幹呢,怎麼會有這種Bug
,我輸的明明是空格呀,我的空格哪去了呢...
果然多看文檔纔是王道呀
人家大大的 Important 寫着 加了 tabindex=0的元素也就意味着會是以按鈕形式顯示的元素,必須是響應 Enter 和 Spacebar 鍵的
這樣一看,爲什麼我加了點擊和關閉事件以後,默認回車和空格鍵就生效了,你找個 Accessibility 做的好的頁面,隨便 Tab 一個 Button,人家按回車和空格都是可以 work 的呀。
自然,在我沒有自定義任何響應空格鍵的方法的情況下,輸入框裏輸入空格,它不關閉我的 Listbox 纔怪呢。
那麼這下我就知道該做什麼了,自定義按空格鍵的事件把默認的覆蓋不就好了。
//僞代碼
<div tabindex="0" role="listbox">
<input onKeyDown={ this.onKeyDown} ref={input => {this.textBox=input;}}
/>
</div>
//僞代碼
onKeyDown = event => {
if(event.keyCode === 32) {
event.preventDefault(); //禁用默認的打開關閉事件
this.input.value = this.input.value.concat(' '); //更新 input 輸入的值爲添加了空字符串的 string
}
}
看着很健康,很完美,誘人的空格就這樣出來了。
但是我發現好像忘了按空格了,讓我再回去加個空格。
崩了,我還能說什麼呢,只傻傻的給後面追加空字符串是真的不行啊,無論在哪裏按空格,都是在最後面追加。
正確的思路應該是這樣的
- 獲取光標所在的位置
- 在光標的位置加空格,重新拼接輸入的字符串
- 更新 input 的值
- 讓添加空格之後的光標位置保持的原位置
但是要怎麼獲取光標的位置,得解決這個問題,搜了很多資料加動了一些小腦筋之後,才發現兩行代碼就搞定了,這下才是真的完美。
if(event.KeyCode === 32) {
event.preventDefault(); //同樣禁用默認的打開關閉事件
const caretIndex = this.input.selectionStart; //獲取光標的位置,其實是用了做選中一段文字的起始位置的接口
const inputedText = `${this.input.value.slice(0, caretIndex)} ${this.input.value.slice(caretIndex)}`; //將空字符串加到光標所在的位置
this.input.value = inputedText; //更新 input 的值
this.setState({ //這時候默認加的空格是不會觸發 input 的 onchange 事件的,同樣需要手動 trigger
inputedText,
});
this.input.focus(); //從這裏開始是移動光標到加了空格的地方,如果沒有這兩行,那麼添加完空格之後,光標默認還是會回到輸入值的最後
this.input.selectionEnd = caretIndex + 1; //還是用了做選中一段文字的相關結果,不過是設置結束位置的光標
// this.input.setSelectionRange(caretIndex + 1, caretIndex + 1); 同樣也可以用這種思路實現,將要選擇的範圍起點的終點設在同一個位置
}
實現一個空格鍵的功能並沒有那麼難吧,總之,還有兩個比較重要的點。
在用一個自己不那麼熟悉的東西的時候,不管你開始看的是中文的還是哪裏的文檔,最終一定要仔細看看官方的文檔,坑很容易就從眼皮底下溜了的
在網上搜答案的時候,不要一味複製粘貼,發現一個不行,兩個還是不行,稍微搜一下用法的文檔,自己再多想那麼一丟丟,可能問題一下子就有了答案。