来源:
https://www.cns11643.gov.tw/wordView.jsp?ID=90944)
那什麽是 keyCode 呢?既然 charCode 代表着是一个 char(字符)的 code,那 keyCode 显然就是代表一个 key(按键)的 code。
每一个「按键」也都有一个它自己的代码,而且有时候会让你混淆,因为它跟 charCode 可能是一样的。
举例来说:「A」这个按键的 keyCode 是 65,而「A」这个字符的 charCode 也是 65。这应该是为了某种方便性所以这样设计,但你要注意到一点:
当我按下「A」这个按键的时候,我可能要打的是 a 或是 A,有两种可能。
或是举另外一个例子,当你要打数字 1 时,如果你是用 Q 上方的那颗按键而不是用纯数字键盘,你要打的字可能是「1」或是「!」或甚至是「ㄅ」,因为它们都是同一颗按键。
一个按键对应了不只一个字符,所以单单从 keyCode,你是没办法判断使用者想打什麽字的。
讲到这裡,我们可以来想一下这两个跟 keyPress 与 keyDown 的关联了。
刚刚说到 keyPress 是你要输入文字的时候才会触发,所以这个事件会拿到 charCode,因为你要知道使用者打了什么字。那为什么不是 keyCode 呢?因为你从 keyCode 根本不知道他打了什么字,所以拿 keyCode 也没用。
keyDown 则是在你按下任何按键时都会触发,这时候一定要拿 keyCode,因为你要知道使用者按了什么按键。若是拿 charCode 的话,你按 shift 或是 ctrl 就没有值了,因为这不是一个字符,就没办法知道使用者按了什么。
总结一下,当你要侦测使用者输入文字的时候,就用 keyPress,并且搭配 charCode 来看使用者刚刚输入了什么;当你想侦测使用者「按下按键」的时候,就用 keyDown,搭配 keyCode 获得使用者所按下的按键。
这就是 keyPress、keyDown 以及 keyCode 跟 charCode 的差别。
顺带一提,在输入中文的时候 keyPress 不会有值,keyDown 则会回传一个神秘的代码 229:
key 与 which
在 keyPress 与 keyDown 这两个 event 里面,其实还有两个属性:key 与 which。
我们先来看一下 which 是什麽:
The which read-only property of the KeyboardEvent interface returns the numeric keyCode of the key pressed, or the character code (charCode) for an alphanumeric key pressed.
来源:
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/which
根据我自己的理解, 当你在 keyPress 里面用 which 的时候,拿到的应该就是 charCode;在 keyDown 里面用的时候就是 keyCode,所以你在写程序的时候可以统一用 event.which 来拿这个信息,不必再区分 keyCode 或是 charCode。
不过 MDN 附的参考资料写的蛮模糊的,所以这部分我也不是很确定:
which holds a system- and implementation-dependent numerical code signifying the unmodified identifier associated with the key pressed. In most cases, the value is identical to keyCode.
来源:
www.w3.org
接着來看一下 key:
The KeyboardEvent.key read-only property returns the value of the key pressed by the user while taking into considerations the state of modifier keys such as the shiftKey as well as the keyboard locale/layou
来源:
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
简单来说 key 会是一个字符串,你刚刚按了什么按键或是打了什么字,key 就会是什麽。上面 MDN 的网页下方有附一个简单的范例让你来测试 key 的值。
例如说我输入 A,key 就是 A,按下 Shift,key 就是 Shift。
还有一点要注意的是,这个属性在 keyPress 或是 keyDown 事件里面都拿得到。所以尽管是 keyDown 事件,你也能知道使用者刚刚输入了什麽或是按了什麽按键。
但尽管如此,关于「侦测输入」的事件应该还是用 keyPress 最合适,除非你想要侦测其他不会产生字符的按键(Ctrl, Delete, Shift…)才用 keyDown 事件。
在这边做个中场总结,其实这些 which、keyCode 跟 charCode,在不同浏览器上面都可能有不同的表现,所以是跨浏览器支持一个很麻烦的部分,从这个方向去找,你可以找到一大堆在讲浏览器相容性的文章。
但近几年来旧的浏览器渐渐被淘汰,大部分的使用者在用的浏览器应该都比较符合标准了,因此相容性并不是本篇文章的重点,所以就没有多提了。
接下来终于要到可能是最吸引你的部分:React 原代码。
初探 React 原代码
React 原代码这麽大,该从何找起呢?
这边推荐一个超级好用的方法:GitHub 的搜寻。通常只要拿你想找的 function 名称或是相关的关键字下去搜寻,就能够把范围限缩的很小,只要用肉眼再翻一下资料就能够找到相对应的原代码,是方便又好用的一个方法。
这边我们用 keyPress 来当关键字,出现了 12 笔结果:
用肉眼稍微筛选一下,发现很多都是测试,那些都可以直接跳过。你应该很快就能定位到几个相关的档案,像是这两个:
-
packages/react-dom/src/events/SyntheticKeyboardEvent.js
-
packages/react-dom/src/events/getEventKey.js
没错,这两个就是今天的主角。
我们先来看
SyntheticKeyboardEvent.js
,如果你对 React 还算熟悉的话,应该知道你在里面拿到的事件都不是原生的事件,而是 React 会包装过之后再丢给你,而现在这个
SyntheticKeyboardEvent
就是经过 React 包装后的事件,就是你在 onKeyPress 或是 onKeyDown 的时候会拿到的 e。
为了方便起见,我们切成几个 function,一个一个来看。
charCode: function(event) {
if (event.type === 'keypress') {
return getEventCharCode(event);
return 0;
}
这边注释写得很棒,説 keyPress 已经被 deprecated 了但是替代品还没准备好。再者,也提到了只有 keyPress 有 charCode。
所以这边就是判断 event 的 type 是不是 keypress ,是的话就回传
getEventCharCode(event)
,否则回传 0。
接著我们来看一下
getEventCharCode
在做什么(小提醒,这个函数在另外一个文件):
接着我们一样分段来看比较方便:
开头的注释先跟你说 charCode 代表的就是 character code,所以可以用
String.fromCharCode
来找出搭配的字符。
因此,只有能被印出来(或者是说可以被显示出来)的字符才有 charCode,而 Enter 是一个例外,因为 Enter 会产生空行。但 Tab 不是,因为你按 Tab 不会产生一个代表 Tab 的字符。
let charCode;
const keyCode = nativeEvent.keyCode;
if ('charCode' in nativeEvent) {
charCode = nativeEvent.charCode;
if (charCode === 0 && keyCode === 13) {
charCode = 13;
} else {
charCode = keyCode;
}
这边针对浏览器的兼容性做处理,FireFox 没有帮 Enter 设定 charCode,所以要额外判断 keyCode 是不是 13。然后 IE8 没有实作 charCode,所以用 keyCode 的值来取代。
if (charCode === 10) {
charCode = 13;
if (charCode >= 32 || charCode === 13) {
return charCode;
}
这边应该算是一个 special case,当使用者按下 Ctrl + Enter 时的 charCode 是 10,React 想把这个也当作按下 Enter 来处理。
另外,有些没办法印出来的字符应该要被拿掉,所以最后做了一个范围的判断。
charCode 的处理就是这样了,仔细看看其实还蛮有趣的,针对浏览器的兼容性跟一些特殊状况做了处理。
接着我们回到
SyntheticKeyboardEvent.js
,来看看 keyCode 的处理:
keyCode: function(event) {
if (event.type === 'keydown' || event.type === 'keyup') {
return event.keyCode;
return 0;
}
这边说 keyCode 的值其实是依赖于键盘的,意思是说有些键盘可能会产生不太一样的 keyCode,但因为大多数美国跟欧洲的使用者都是 US keyboard,所以这边就直接把 keyCode 丢回去而不做特殊处理。
其实这一段我没有看得完全懂,只是大概猜一下意思而已。这边指的「keyboard layout」可能是像 QWERTY 或是 Dvorak 这种的 layout,按键的排列方式完全不同。但如果这样就会产生不同的 keyCode 的话,是不是代表有些网站可能会有 bug?
不过大多数人的键盘都是同样的排列,所以好像不用太担心这个问题。
which: function(event) {
if (event.type === 'keypress') {
return getEventCharCode(event);
if (event.type === 'keydown' || event.type === 'keyup') {
return event.keyCode;
return 0;
}
最后是 which 的部分,如果是 keypress 就把 charCode 传回去,keydown 或是 keyup 的话就把 keyCode 传回去。
讲到这裡,我们已经看到 React 对于 charCode、keyCode 以及 which 的处理了,charCode 针对特殊情形以及 浏览器兼容性做检查,keyCode 直接回传,which 则根据事件不同回传相对应的值。
最后我们来看一下 key 的处理,这边放在另外一个文件叫做
getEventKey.js
: