
从 React 源代码看 keyPress 与 keyDown 事件
keyPress 跟 keyDown 的差异
首先,我们要来看看 keyPress 与 keyDown 这两个原生事件的差异到底在那里,这部分我们直接请出 MDN 来为我们做解释:
The keypress event is fired when a key that produces a character value is pressed down. Examples of keys that produce a character value are alphabetic, numeric, and punctuation keys. Examples of keys that don't produce a character value are modifier keys such as Alt, Shift, Ctrl, or Meta.
来源:https://developer.mozilla.org/en-US/docs/Web/Events/keypress
The keydown event is fired when a key is pressed down.
Unlike the keypress event, the keydown event is fired for all keys, regardless of whether they produce a character value.
来源:https://developer.mozilla.org/en-US/docs/Web/Events/keydown
简单来说呢,keyDown 会在你按下任何按键时触发,但是 keyPress 只会在你按下的按键可以产生出一个字符的时候触发,白话一点就是你按下这按键是在打字。
例如说你按 a,画面上会出现一个字符 a,所以 keyDown 跟 keyPress 都会触发。但如果你按 shift,画面上什麽都不会出现,所以只有 keyDown 会触发。
w3c 提供 了一个很不错的网页:Key and Character Codes vs. Event Types,让你可以自己实验看看。
下图中我输入 a,两者都会触发,接着我按 shift,只会触发 keyDown,再来按 backspace 把文字删掉,也只会触发 keyDown:
所以这两者的差异相信大家应该可以很清楚地知道了,keyDown 可以当作是「按下按键」,keyPress 则当作「输入东西」时会触发的事件。
接著我们来谈谈 keyCode 跟 charCode。
keyCode 与 charCode 的差异
先来谈谈 charCode 好了,或许你有看过 JavaScript 里面有个函数是这样的:
console.log(String.fromCharCode(65)); // A
charCode 其实就是某一个字符所代表的一个号码,或更精确一点地说,就是它的 Unicode 编码。
这边如果不太熟的话可以参考这篇文章:[Guide] 瞭解网页中看不懂的编码:Unicode 在 JavaScript 中的使用。
在 JavaScript 里面也可以用另一个函数拿到字符所对应的编码:
console.log('嗨'.charCodeAt(0)); // 21992
若是你把这 21992 转成 16 进制,会变成 0x55E8,这个其实就是「嗨」的 Unicode:
来源: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 笔结果:
用肉眼稍微筛选一下,发现很多都是测试,那些都可以直接跳过。你应该很快就能定位到几个相关的档案,像是这两个:
没错,这两个就是今天的主角。
我们先来看 SyntheticKeyboardEvent.js