最近在做 Accessibility 相關(guān)的東西時候,又發(fā)現(xiàn)了一個坑,就是跟這個看似完美的tabindex=0
的有關(guān)笔诵。
其實究其原因,還是年輕的我不仔細(xì)看文檔導(dǎo)致的姑子,但我覺得文檔沒有特別強調(diào)的但是確實很重要的東西就有可能是未來的坑乎婿,所以還是想分享一下給大家。
要實現(xiàn)的需求大概是這樣的
有一個選擇框是這個樣子的街佑,我們叫它 可以搜索的 Listbox
谢翎,可以點擊打開里面的List
選擇,也可以點擊輸入你想要的國家英文名稱沐旨,它會模糊進行 Filter
給出你想要的結(jié)果森逮。
但我們今天想說的不是它的搜索和選擇功能,而是你看到的這個組件的無障礙可訪問功能(Web Accessibility)中的輸入空格的功能磁携。
可能你還是不是太明白褒侧,再看個圖吧,我在按下了 Tab
鍵之后,這個 Listbox
會被聚焦闷供,并且有藍色的邊框烟央,這個就是用這行代碼實現(xiàn)的。
//偽代碼
<div tabindex="0" role="listbox">
<input tabindex="-1" />
</div>
在我添加了一系列點擊事件之后歪脏,我發(fā)現(xiàn)一旦它被Focus
之后疑俭,按下空格和回車鍵的時候,它就會依次打開婿失、關(guān)閉怠硼,心中竊喜,簡直完美移怯,也沒有仔細(xì)看原因.......
然后呢,再測試的時候就有問題了这难,當(dāng)我在輸入框輸入一串字符舟误,接著想輸入一個空格的時候,我的Listbox
直接就給我合上了姻乓,這是怎么回事呢嵌溢,什么都沒干呢,怎么會有這種Bug
蹋岩,我輸?shù)拿髅魇强崭裱嚼挡荩业目崭衲娜チ四?..
果然多看文檔才是王道呀
人家大大的 Important 寫著 加了 tabindex=0的元素也就意味著會是以按鈕形式顯示的元素,必須是響應(yīng) Enter 和 Spacebar 鍵的
這樣一看剪个,為什么我加了點擊和關(guān)閉事件以后秧骑,默認(rèn)回車和空格鍵就生效了,你找個 Accessibility 做的好的頁面扣囊,隨便 Tab 一個 Button乎折,人家按回車和空格都是可以 work 的呀。
自然侵歇,在我沒有自定義任何響應(yīng)空格鍵的方法的情況下骂澄,輸入框里輸入空格,它不關(guān)閉我的 Listbox 才怪呢惕虑。
那么這下我就知道該做什么了坟冲,自定義按空格鍵的事件把默認(rèn)的覆蓋不就好了。
//偽代碼
<div tabindex="0" role="listbox">
<input onKeyDown={ this.onKeyDown} ref={input => {this.textBox=input;}}
/>
</div>
//偽代碼
onKeyDown = event => {
if(event.keyCode === 32) {
event.preventDefault(); //禁用默認(rèn)的打開關(guān)閉事件
this.input.value = this.input.value.concat(' '); //更新 input 輸入的值為添加了空字符串的 string
}
}
看著很健康溃蔫,很完美健提,誘人的空格就這樣出來了。
但是我發(fā)現(xiàn)好像忘了按空格了伟叛,讓我再回去加個空格矩桂。
崩了,我還能說什么呢,只傻傻的給后面追加空字符串是真的不行啊侄榴,無論在哪里按空格雹锣,都是在最后面追加。
正確的思路應(yīng)該是這樣的
- 獲取光標(biāo)所在的位置
- 在光標(biāo)的位置加空格癞蚕,重新拼接輸入的字符串
- 更新 input 的值
- 讓添加空格之后的光標(biāo)位置保持的原位置
但是要怎么獲取光標(biāo)的位置蕊爵,得解決這個問題,搜了很多資料加動了一些小腦筋之后桦山,才發(fā)現(xiàn)兩行代碼就搞定了攒射,這下才是真的完美。
if(event.KeyCode === 32) {
event.preventDefault(); //同樣禁用默認(rèn)的打開關(guān)閉事件
const caretIndex = this.input.selectionStart; //獲取光標(biāo)的位置恒水,其實是用了做選中一段文字的起始位置的接口
const inputedText = `${this.input.value.slice(0, caretIndex)} ${this.input.value.slice(caretIndex)}`; //將空字符串加到光標(biāo)所在的位置
this.input.value = inputedText; //更新 input 的值
this.setState({ //這時候默認(rèn)加的空格是不會觸發(fā) input 的 onchange 事件的会放,同樣需要手動 trigger
inputedText,
});
this.input.focus(); //從這里開始是移動光標(biāo)到加了空格的地方,如果沒有這兩行钉凌,那么添加完空格之后咧最,光標(biāo)默認(rèn)還是會回到輸入值的最后
this.input.selectionEnd = caretIndex + 1; //還是用了做選中一段文字的相關(guān)結(jié)果,不過是設(shè)置結(jié)束位置的光標(biāo)
// this.input.setSelectionRange(caretIndex + 1, caretIndex + 1); 同樣也可以用這種思路實現(xiàn)御雕,將要選擇的范圍起點的終點設(shè)在同一個位置
}
實現(xiàn)一個空格鍵的功能并沒有那么難吧矢沿,總之,還有兩個比較重要的點酸纲。
在用一個自己不那么熟悉的東西的時候捣鲸,不管你開始看的是中文的還是哪里的文檔,最終一定要仔細(xì)看看官方的文檔闽坡,坑很容易就從眼皮底下溜了的
在網(wǎng)上搜答案的時候栽惶,不要一味復(fù)制粘貼,發(fā)現(xiàn)一個不行疾嗅,兩個還是不行媒役,稍微搜一下用法的文檔,自己再多想那么一丟丟宪迟,可能問題一下子就有了答案酣衷。