近,知乎在桌面端網(wǎng)頁(yè)上陸續(xù)增加了一些快捷鍵:
在非輸入狀態(tài)下按 ?(Shift+/)就可以查看快捷鍵列表
在開(kāi)發(fā)快捷鍵的過(guò)程中,有一些值得分享的經(jīng)驗(yàn)。
實(shí)現(xiàn)快捷鍵不困難,因?yàn)楸O(jiān)聽(tīng)鍵盤(pán)事件很簡(jiǎn)單。大部分時(shí)候,我們只需要在希望響應(yīng)事件的最外層元素上注冊(cè)事件就行,而響應(yīng)事件的回調(diào)(比如關(guān)閉模態(tài)框)也都可以在這個(gè)最外層組件內(nèi)實(shí)現(xiàn)。
知乎首頁(yè)的組件結(jié)構(gòu)
但是這次的情況要更加復(fù)雜。以在首頁(yè)按 V 贊同答案為例:列表內(nèi)的時(shí)間線條目(FeedItem)包括了回答(AnswerItem)在內(nèi)各種類(lèi)型的內(nèi)容,而在回答里的贊同按鈕(VoteButton)才是適合響應(yīng)快捷鍵事件的地方。在最外側(cè)的 FeedItem 上容易注冊(cè)事件卻無(wú)法響應(yīng)事件,在最內(nèi)側(cè)的 VoteButton 中可以響應(yīng)事件卻不能簡(jiǎn)單地注冊(cè)事件。
如何在 React 的組件關(guān)系中優(yōu)雅地實(shí)現(xiàn)子組件(贊同、收藏按鈕等)注冊(cè)快捷鍵和回調(diào)函數(shù)、同時(shí)直到父組件(整個(gè)回答甚至 Feed 條目)的范圍內(nèi)都可以響應(yīng)快捷鍵是比較有趣的。
一種簡(jiǎn)單的想法是:放棄在子組件中注冊(cè)快捷鍵,全部在父組件中注冊(cè)快捷鍵,在回調(diào)的時(shí)候調(diào)用子組件的實(shí)例方法。這會(huì)造成以下這些問(wèn)題:
現(xiàn)在知乎使用的方案是:基于 React context 來(lái)為所有子組件共享一個(gè)「快捷鍵實(shí)例」,在這個(gè)「作用域」下的所有子組件聲明快捷鍵時(shí),都會(huì)注冊(cè)到這個(gè)實(shí)例上面。
參考下方的偽代碼:
function FeedItem() {
const feedItemElement = useRef(null)
return (
<ShortcutContext value={feedItemElement}>
<div className="FeedItem" ref={feedItemElement}>
<AnswerItem />
</div>
</ShortcutContext>
)
}
在 FeedItem 中,以自身的 HTML element 來(lái)初始化一個(gè)新的快捷鍵實(shí)例并設(shè)置在 context 中,實(shí)際的 keydown 等鍵盤(pán)事件是注冊(cè)在這個(gè) element 上。所以子組件都可以通過(guò) context 在這個(gè)實(shí)例上注冊(cè)新的快捷鍵。
function VoteButton() {
const handleVote = () => console.log('voting')
// 在 useShortcut 中查找當(dāng)前 context 中存在的快捷鍵實(shí)例,并注冊(cè)在實(shí)例上
useShortcut('v', handleVote)
return <button onClick={handleVote}>贊同</button>
}
在 VoteButton 中,就可以像響應(yīng)點(diǎn)擊事件一樣注冊(cè)并響應(yīng)快捷鍵事件,看起來(lái)和寫(xiě)起來(lái)都非常清爽。
使用 J、K 進(jìn)行 Vim 風(fēng)格的列表導(dǎo)航是許多網(wǎng)站常見(jiàn)的快捷鍵設(shè)計(jì)[2]。下面是一些如果從零開(kāi)始實(shí)現(xiàn)時(shí),可能會(huì)遇到的細(xì)節(jié)問(wèn)題:
當(dāng)使用快捷鍵進(jìn)行瀏覽時(shí),「知道光標(biāo)在哪里」很重要。屏幕閱讀器可以閱讀當(dāng)前 focus 元素的內(nèi)容。如果不通過(guò)聲音的話(huà)、就只能通過(guò)視覺(jué)樣式來(lái)知道當(dāng)前正在 focus 的元素是什么。
Chrome 為 Input 標(biāo)簽?zāi)J(rèn)添加的 focus outline
瀏覽器默認(rèn)會(huì)為可 focus 的元素通過(guò) :focus 偽類(lèi)增加一個(gè) outline 樣式,因?yàn)檫@個(gè)樣式不是很好看,再加上可 focus 的元素往往也會(huì)單獨(dú)設(shè)計(jì)一些響應(yīng)點(diǎn)擊的樣式,所以一般產(chǎn)品或設(shè)計(jì)會(huì)要求工程師取消掉該樣式。但是如果簡(jiǎn)單取消所有 focus 樣式后,用鼠標(biāo)當(dāng)然知道「我在點(diǎn)哪里」,但在使用鍵盤(pán)訪問(wèn)的場(chǎng)合就根本不知道「我在哪里」了。
最優(yōu)雅的方案是使用 CSSWG 的 :focus-visible 偽類(lèi)來(lái)添加 focus 樣式(同時(shí)禁用原先的 :focus 樣式),在 WICG 對(duì)該樣式的 polyfill 中有詳細(xì)的介紹。簡(jiǎn)單來(lái)說(shuō),就是「只有用鍵盤(pán)觸發(fā)的 focus 才應(yīng)該添加 focus 樣式」。
知乎按鈕的 focus 樣式
知乎通過(guò)和該 polyfill 類(lèi)似的思想實(shí)現(xiàn)了這一設(shè)計(jì):在使用鍵盤(pán)操作后,會(huì)為 <html> 元素添加 data-focus-visible 屬性。只有在包含該屬性的情況下,各個(gè)元素才會(huì)添加 focus 樣式。而且知乎還修改了默認(rèn)的樣式、更為美觀。你可以使用 Tab 鍵 tab 到贊同按鈕上查看這個(gè)效果。
在開(kāi)發(fā)這一部分功能的時(shí)候,還有一個(gè)特殊的設(shè)計(jì):使用 ?+C 等快捷鍵進(jìn)行復(fù)制粘貼操作(準(zhǔn)確地說(shuō),是按鍵中包括 Control 或 Shift 等 Modifier Key)時(shí),不認(rèn)為是一般的鍵盤(pán)操作,也不展現(xiàn) focus outline,否則 outline 會(huì)過(guò)于頻繁地時(shí)有時(shí)無(wú)。有意思的是,Twitter 其實(shí)也已經(jīng)做了類(lèi)似的處理,讓人感到大洋兩岸的工程師都在為用戶(hù)體驗(yàn)而努力…
很多關(guān)于快捷鍵的討論都會(huì)有視障用戶(hù)的參與,因?yàn)槭褂闷聊婚喿x器瀏覽網(wǎng)頁(yè)、必須使用包括 Tab 鍵在內(nèi)的各種鍵盤(pán)快捷鍵進(jìn)行光標(biāo)定位與操作,他們是「最會(huì)用鍵盤(pán)刷知乎」的人。但是,對(duì)視障用戶(hù)的支持遠(yuǎn)不止添加快捷鍵這么簡(jiǎn)單。
@devil纏
在一個(gè)答案的評(píng)論區(qū)[6]提到:
如果只是單個(gè)按鍵,(快捷鍵)基本上沒(méi)有任何用處。以 V 為例:當(dāng)用 Tab 到達(dá)「贊同」按鈕時(shí),直接(按)空格就可以點(diǎn)贊同。(另外)如果在查看內(nèi)容區(qū)點(diǎn)擊 V,焦點(diǎn)不會(huì)跑到「贊同」按鈕上。
合理的快捷鍵有可能用處不大,而不合理的快捷鍵不但不能幫助視障用戶(hù),還會(huì)幫倒忙。
@殷曉波
和
@devil纏
都提到「使用 V 贊同之后,希望可以 focus 到贊同按鈕」,如果不真正使用屏幕閱讀器瀏覽網(wǎng)頁(yè),是無(wú)法想象這句話(huà)的原因的:
快捷鍵贊同后,需要轉(zhuǎn)移焦點(diǎn)
簡(jiǎn)單來(lái)說(shuō),屏幕閱讀器只有在焦點(diǎn)改變時(shí)才會(huì)閱讀焦點(diǎn)內(nèi)的文字,它監(jiān)控不到「贊同按鈕變深藍(lán)」這樣普通人可以輕松理解的設(shè)計(jì)反饋。如果使用快捷鍵贊同后不改變焦點(diǎn),連按下鍵盤(pán)發(fā)生了什么都不知道,也就不會(huì)知道「已經(jīng)贊同了答案」。此時(shí) tab 到贊同按鈕上閱讀到「已贊同」的文字才知道發(fā)生了什么就很奇怪。
瀏覽器的點(diǎn)擊行為會(huì)自動(dòng) focus 在可交互的元素上(例如 <button> 或 <a>),而此時(shí)按 Enter 或 Space 等快捷鍵可以「模擬一次點(diǎn)擊」,這套現(xiàn)成的體系很容易被忽略。在實(shí)際體驗(yàn)中,tab 到「閱讀全文」按鈕再按 Space 來(lái)展開(kāi)全文并不比用快捷鍵 O 來(lái)展開(kāi)全文麻煩很多。
除此之外,很多讀屏軟件或者視障用戶(hù)也會(huì)定義、開(kāi)發(fā)個(gè)性化的快捷鍵[6]。這么來(lái)看,使用 <button> 等語(yǔ)義化標(biāo)簽使元素可交互元素也可以被 focus、盡可能使用 <a> 而不是在監(jiān)聽(tīng) onClick 事件時(shí)使用 location.href 進(jìn)行頁(yè)面跳轉(zhuǎn)、配置好 aria 屬性等…對(duì) Accessibility 更有意義。
總的來(lái)說(shuō),實(shí)現(xiàn)快捷鍵和實(shí)現(xiàn)其他功能一樣也要注意視障用戶(hù)的使用、交互體驗(yàn),比如:
快捷鍵只是 Accessibility 的一部分,而可訪問(wèn)性又是一個(gè)更加系統(tǒng)和復(fù)雜的工程。知乎做了一些努力[7],但還遠(yuǎn)遠(yuǎn)不夠。也歡迎對(duì)這個(gè)領(lǐng)域有更多了解的朋友提出建議。
是的。如果你使用 Vimperator 或者 Vimium 等瀏覽器擴(kuò)展定制了快捷鍵而不想和知乎的沖突,可以在桌面端網(wǎng)頁(yè)的個(gè)人偏好設(shè)置(https://www.zhihu.com/settings/preference)中關(guān)閉快捷鍵[8]。
這是編輯器的新功能,會(huì)在開(kāi)放后再行介紹。
^這些快捷鍵在遷移到新版 Web 頁(yè)面時(shí)沒(méi)有同步遷移,在很長(zhǎng)一段時(shí)間內(nèi)都沒(méi)有實(shí)現(xiàn)。
^包括 Twitter、Facebook、Gmail 與新浪微博等,知乎從這些網(wǎng)站的實(shí)現(xiàn)細(xì)節(jié)中受益良多。
^一個(gè)叫 tabbable 的庫(kù)中有關(guān)于這兩者區(qū)別的介紹,這個(gè)庫(kù)在實(shí)現(xiàn) focus trap 等效果時(shí)很有用。 https://github.com/davidtheclark/tabbable
^如何高效地查找離視窗最近、滾動(dòng)距離最小的元素,這個(gè)算法比較有意思,這里不贅述了。
^知乎和 Facebook 與新浪微博一樣,會(huì)選中視野內(nèi)可見(jiàn)的新元素,而 Twitter 會(huì)放棄滾動(dòng)。
^ab根據(jù) @devil纏 在這個(gè)答案評(píng)論區(qū)中的說(shuō)法,他使用的讀屏環(huán)境還會(huì)定義包括 K 下跳 10 個(gè)鏈接、Shift+K 上跳 10 個(gè)鏈接等快捷鍵 https://www.zhihu.com/question/19842222/answer/17152043
^@長(zhǎng)天之云 的答案介紹了一些知乎對(duì) a11y 的支持 https://www.zhihu.com/question/20487917/answer/15265930
^只在當(dāng)前使用的瀏覽器中生效。
作者:孫北吉
出處:https://zhuanlan.zhihu.com/p/59928288
在的瀏覽器普遍都支持Javascript,客戶(hù)的瀏覽器都安裝了好幾種。這里我們對(duì)javascipt做個(gè)初步的介紹。
javascript是一種基于對(duì)象和事件并具有安全性能的解釋型腳本語(yǔ)言。不但用于編寫(xiě)客戶(hù)端的腳本程序,由客戶(hù)端瀏覽器解釋執(zhí)行。而且還可以編寫(xiě)在服務(wù)器端執(zhí)行的腳本程序,由服務(wù)器端處理用戶(hù)提交的信息并動(dòng)態(tài)地向?yàn)g覽器返回處理結(jié)果。
字符串型:使用單引號(hào)或者雙引號(hào)括起來(lái)的一個(gè)或者多個(gè)字符串
數(shù)值型:包括整數(shù)或者浮點(diǎn)數(shù)(包含小數(shù)點(diǎn)的數(shù)或者科學(xué)計(jì)數(shù)法的數(shù))
布爾型:true或者false
對(duì)象型:用于指定javascript程序中用到的對(duì)象
空值:給一個(gè)變量賦值null值來(lái)清除變量的內(nèi)容
undefined:表示該變量尚未被賦值
1、變量命名的規(guī)則
必須以字母或者下劃線開(kāi)頭,中間可以是數(shù)字、字母或者下劃線。
變量名不能包含空格或加號(hào)、減號(hào)等符號(hào)。
變量名嚴(yán)格區(qū)分大小寫(xiě)的。大小寫(xiě)不一樣代表不同的變量。
變量名不能使用關(guān)鍵字。
關(guān)鍵字和java的關(guān)鍵字類(lèi)似。這里不列舉。
2、聲明和賦值
var variable; //聲明一個(gè)變量
var variable=10; //聲明并賦值11
var sum,avg; //聲明多個(gè)變量
var sum=0,avg=10; //聲明多個(gè)變量并賦值
Javascript的函數(shù)和語(yǔ)句和php有相通的地方。這里就不介紹了。
可以閱讀其他相關(guān)教程:https://www.w3school.com.cn/js/index.asp
事件是某些動(dòng)作發(fā)生的時(shí)候產(chǎn)生的信號(hào),這些事件隨時(shí)可以發(fā)生。引起事件的發(fā)生的動(dòng)作成為觸發(fā)事件。比如鼠標(biāo)點(diǎn)擊了某個(gè)按鈕,用戶(hù)在文本框輸入文字都會(huì)有對(duì)應(yīng)的觸發(fā)事件。
下面是常見(jiàn)的事件:
onchange | HTML 元素已被改變 |
onclick | 用戶(hù)點(diǎn)擊了 HTML 元素 |
onmouseover | 用戶(hù)把鼠標(biāo)移動(dòng)到 HTML 元素上 |
onmouseout | 用戶(hù)把鼠標(biāo)移開(kāi) HTML 元素 |
onkeydown | 用戶(hù)按下鍵盤(pán)按鍵 |
onload | 瀏覽器已經(jīng)完成頁(yè)面加載 |
其他事件可以查看手冊(cè):
https://www.w3school.com.cn/js/js_events.asp
https://www.w3school.com.cn/jsref/jsref_events.asp
https://www.w3school.com.cn/jsref/dom_obj_event.asp
1、在html中嵌入javascript腳本
在html中使用<script>標(biāo)記就可以嵌入到html中
<script language="javascript">
....
</script>
language標(biāo)記的是設(shè)置腳本語(yǔ)言的名稱(chēng)和版本,也可以不設(shè)置該屬性,瀏覽器默認(rèn)使用javascript腳本預(yù)約進(jìn)行處理。
2、在網(wǎng)頁(yè)中引用Js文件
不管是在php,還是其他語(yǔ)言上甚至是普通的html頁(yè)面上,都可以使用外置的腳本文件來(lái)調(diào)用javascipt腳本
<script src="url" language="javascript"></script>
src的url是js文件的路徑,language的作用和上面的一樣。使用腳本文件不僅可以在html中結(jié)合使用,還可以與php動(dòng)態(tài)網(wǎng)頁(yè)結(jié)合使用。使用外部js文件的優(yōu)點(diǎn)如下:
將javascript代碼從網(wǎng)頁(yè)中獨(dú)立出來(lái),方便代碼閱讀
一個(gè)外部js文件可以同時(shí)被多個(gè)頁(yè)面調(diào)用。需要修改代碼,只需要修改js文件的代碼,方便代碼維護(hù)。
通過(guò)script不僅可以調(diào)用自己服務(wù)器的上面的js文件,也可以通過(guò)路徑調(diào)用其他服務(wù)器上的js文件。
注意:在js文件中,只能包含javascript腳本代碼,不能包含<script>標(biāo)記和html代碼,不然會(huì)產(chǎn)生錯(cuò)誤。
在引用js文件的<script>和</script>標(biāo)記的中間不能有其他的javascript代碼,即使存在了瀏覽器也是會(huì)忽略這寫(xiě)代碼,而只執(zhí)行js文件中的javascript代碼
3、頁(yè)面中調(diào)用自定義函數(shù)
這個(gè)就是前面說(shuō)道的事件,根據(jù)事件調(diào)用自定義函數(shù)。
<button type="button" onclick="check();"> 提交 </button>
提交按鈕綁定了一個(gè)點(diǎn)擊事件,調(diào)用check()函數(shù)。
1、開(kāi)啟瀏覽器對(duì)Javascript的支持
有些瀏覽器是出于安全考慮關(guān)閉了對(duì)javascript的支持,所以可以直接通過(guò)瀏覽器啟用對(duì)javascript的支持。
2、使用注釋符號(hào)驗(yàn)證瀏覽器的支持
如果用戶(hù)不確定自己的瀏覽器是否支持javascript腳本,可以使用html的注釋符號(hào)來(lái)進(jìn)行驗(yàn)證。Html注釋符號(hào)“<!--”開(kāi)始,以“-->”結(jié)束。如果此注釋符號(hào)里的javascript腳本在不支持javascript的瀏覽器,會(huì)把javascript腳本作為注釋而不會(huì)顯示在客戶(hù)瀏覽器上。
<script type="text/javascript">
<!--
//即使不支持Javascript腳本,用戶(hù)也看不到這里面的代碼
var a={};
a[“stu”] = null;
-->
</script>
3、使用<noscript>標(biāo)記驗(yàn)證瀏覽器的支持
除了上面的方式,還可以使用<noscript>標(biāo)記來(lái)進(jìn)行驗(yàn)證。
如果瀏覽器支持Javascript腳本,那么瀏覽器會(huì)忽略<noscript>...</noscript>標(biāo)記中間的任何內(nèi)容,反之不支持會(huì)把這標(biāo)記中間的內(nèi)容顯示出來(lái)。用來(lái)提醒瀏覽者當(dāng)前使用瀏覽器是否支持Javascript腳本。
文章有
ctrl+s一鍵提交的功能時(shí)候,真的是會(huì)方便很多,對(duì)于我們這些高度使用pc,并且熟悉PC快捷鍵的使用者來(lái)說(shuō),如何實(shí)現(xiàn)html響應(yīng)ctrl+s快捷鍵,下面我分享一個(gè)簡(jiǎn)單的代碼給大家。
$(window).keydown( function (event) {
if(event.ctrlKey && window.event.keyCode==83 ){
$("#submit").click()
return false
}
}) ;
當(dāng)使用ctrl+s組合快捷鍵的時(shí)候,我們模擬了submit的點(diǎn)擊事件進(jìn)行提交文章。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。