事件已弃用的KeyboardEvent.which属性的替代方法

时间:2018-03-14 13:10:10

标签: javascript events keyboard-events

MDN声明{@ 3}}已被弃用。如何将其替换为非弃用版本?

例如,我有以下内容:

window.onkeydown = (event) => { console.log(event.which); }

我认为event.key.charCodeAt()可以替换event.which,但这不适用于 ALT CTRL 等键,它只适用于event.key.length === 1

window.onkeydown = (event) => { console.log(event.key.charCodeAt()); }

总结一下,event.which != event.codeevent.which != event.key,因此我无法简单地使用event.key

是否有替代event.which检测组合按键,包括 ALT CTRL ENTER

3 个答案:

答案 0 :(得分:7)

TL; DR:这些是您应该遵循的规则:

  • 从用户获取文本输入时,将keypress事件与e.key一起使用
  • 对于快捷方式和其他组合,内置方法是使用keydown / keyup并检查各种修饰键。如果需要检测和弦,则可能需要构建状态机。

背景

键盘输入分为两个阶段-键盘按下/键盘输入对,用于跟踪按下的物理键,以及组合多个键序列以计算字符的组合字符。

获取“文本”

如果您想知道操作系统认为组合序列是什么,则应使用KeyboardEvent.key

示例代码:

document.getElementById('a').addEventListener('keypress', e => console.log(e.key));
<input id="a" type="text" placeholder="type here">

大多数时候想要这样做的原因是因为许多语言通过几次按键组成字符。与仅按下shift相比,US-101键盘最容易理解的是按下a键+ Aa。对于像俄语这样的带有altgr死键的语言,这尤其重要。

我想说明的一点是,您自己完成所有这些工作-检测按键序列并确定正确的文本输出是一个难题。这是出于操作系统的原因。

现在,对于较旧的浏览器,由于缺少较旧的支持,您可能不希望使用e.key。然后,您可以使用which之类的东西,或其他非标准方法。

获取击键

现在,假设您不是在跟踪文本,而是在跟踪按键序列。这是用于游戏或听ctrl-c等事物的情况。在这种情况下,正确的做法是听keydown / keyup events。对于修饰键,您可以简单地监听事件的ctrlKeyshiftKeymetaKey属性。见下文:

document.getElementById('a').addEventListener('keydown', (e) => {
  const states = {
    alt: e.altKey,
    ctrl: e.ctrlKey,
    meta: e.metaKey,
    shift: e.shiftKey,
  };
  const key = e.key;
  const code = e.code;
  console.log(`keydown key: ${key}, code: ${code}`, states);
});
<input id="a" type="text" placeholder="press ctrl">

例如,当按下键盘上的shift-o时,我得到以下信息:

keydown key: Shift, code: ShiftLeft {
  "alt": false,
  "ctrl": false,
  "meta": false,
  "shift": true
}
keydown key: O, code: KeyS {
  "alt": false,
  "ctrl": false,
  "meta": false,
  "shift": true
}

希望states部分是不言而喻的。他们说在另一个键按下时是否按下了该修饰键。

keycode之间的差异与键盘布局有关。我正在使用软件dvorak布局。因此,当我键入s键时,进入操作系统的扫描代码显示为s,但是由于它是dvorak,因此操作系统将其转换为o。在这种情况下,代码始终指的是扫描代码(按下了物理键),而该键对应于操作系统的最大努力,以找出“文本”将是什么。这并非总是可能的,尤其是对于其他语言。同样,这就是为什么使用keypress的密钥是正确的解决方法的原因。

第三方库

如果听起来不太容易,那是因为事实并非如此。上次查看此内容时,我遇到了mousetrap库,尽管由于发现了一些问题,我不确定我会推荐它。但是,它确实显示了构建状态机来跟踪和弦的示例。

附录

这也是为什么要吃键盘按键需要跟踪keydown / keyup的原因。由于ctrl + c没有“文本”,因此您将无法获得正确的按键,因此浏览器将对其进行本地处理。如果要运行自己的行为,则需要e.preventDefault()按下按键本身。 (诸如copy之类的一些后续事件也可以取消,但这并非普遍如此)

如果您还只需要跟踪事后插入到输入字段(或可编辑内容的div)中的键,请参阅input事件。

答案 1 :(得分:2)

根据规范:

  

其中类型为无符号长,只读

     
    

包含依赖于系统和实现的数字代码,表示与按下的键关联的未修改标识符。在大多数情况下,该值与keyCode相同

  
     

密钥代码,类型为无符号长,只读

     
    

keyCode包含一个与系统和实现有关的数字代码,表示与按下的键关联的未修改标识符。与KeyboardEvent.key属性不同,此规范中未规范定义可能的值集。通常,keyCode的这些值应代表ASCII [RFC20] [US-ASCII]或Windows 1252 [WIN1252]中的十进制代码点,但可以从其他适当的字符集中得出。无法识别键的实现使用键值“ 0”。

         

有关如何确定keyCode值的更多详细信息,请参见旧式密钥模型。

  

(我已经省略了链接)

因此,创建与规范兼容的版本非常容易。最简单的版本只需为每个键返回0

涉及程度稍高的版本采用event.key并获取其ASCII码。您可以对控制键(altKeyctrlKeyshiftKey)执行相同的操作。

简而言之,which的行为在系统和浏览器之间是不同的。使用未弃用的事件信息,您可以创建一个更强大的版本,以消除这些差异并为将来提供更多的证明。

您可以通过在主要浏览器中实施which来检查版本的行为。如果您不使用任何极端情况,则您的版本将既简单又兼容。

答案 2 :(得分:1)

正如其他答案所指出的那样,event.which有一个主要问题:对于不同的浏览器或计算机,它不会返回相同的数字(也许这是过时的原因)。因此,没有完美的替代方法,因为它将为不同的用户输出不同的数字。

因此,尝试为其创建替代项(命名为function whichSubstitute(event))的主要问题是 Meta Shift 键,则没有whichSubstitute按下其中一个it varies according to OS时应获得的唯一编号。

考虑到这一点,有两种方法可以获取用户输入的unicode代码点。

  1. 获取用户输入的字符的unicode值(例如ü,即'ü'.codePointAt(0))。
  2. 获取与键盘上按下的物理键相对应的字符的数值,该数值可能与输入到文本字段的数值不同。如AnilRedShift所述,键盘布局可能会更改键盘中该键的“自然”输出,以使键 s 可能会输出o。在这种情况下,我们将获得's'.codePointAt(0),而不是获得'o'的值(即实际输出的值),就像使用第一种方法一样。 More on this from MDN
  

例如,返回的代码是“ KeyQ”,用于QWERTY布局键盘上的“ q”键,但是相同的代码值也代表“ '”键在Dvorak键盘上,并在AZERTY键盘上使用“ a”键。如果用户未使用预期的键盘布局,就无法使用代码的值来确定键的名称。

简而言之:方法1获得ü的unicode代码点,而方法2获得 SHIFT 6 U (因为 SHIFT + 6 + U == ü)。

在此答案中,我们将使用String.prototype.codePointAt()而不是String.prototype.charCodeAt()The differences are well explained here。原因是我们可以使用.codePointAt(0)获得整个unicode号,而.charCodeAt(0)则缺少.codePointAt(1)来完成UTF-16编码的代码点。

对于方法数字1 ,我们可以使用以下代码:

function whichSubstitute(event) {
  const theKey = event.key;
  if (theKey.length === 1) {
    return theKey.codePointAt(0);
  }
  switch (theKey) {
    case "Backspace":
      return 8;
    case "Tab":
      return 9;
    case "Enter":
      return 13;
    case "Alt":
      return 18;
    case "Escape":
      return 27;
    case "Delete":
      return 127;

    case "Dead": //As of july 2018, Firefox has no support for "Dead" keys https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
      {}
      break;
    case "Unidentified":
      alert("handle the 'Unidentified' if you want to!");
  }

  /*** there are many other possible key values https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
        but, AFAIK, there are no unicode code points for them, such as:

  switch (theKey) {
    case "AltGraph":
    case "CapsLock":
    case "Control":
    case "Fn":
    case "FnLock":
    ...

       event.which may output some number for them, but it is not consistent across
       browsers/machines and they may overlap with other code points. For example:

  case "ArrowUp":
    return 38; //this overlaps with "&"
  case "ArrowLeft":
    return 37; //this overlaps with "%"
  case "ArrowDown":
    return 40; //this overlaps with "("
  case "ArrowRight":
    return 39; //this overlaps with "'"

  ***/

  return 0;
}

//test
document.onkeydown = (event) => {
  console.log('whichSubstitute: ' + whichSubstitute(event) + '; event.which: ' + event.which);
  //note that whichSubstitute gets the ASCII number of 'a', while event.which only gets the ASCII number of 'A' (upper case, always)
}

当然,这不能解决在没有Unicode代码点的情况下,仅按一个唯一的一致编号的问题。 ( Meta )。此类密钥需要程序员根据自己的需要进行处理。

对于方法 2号,我们可以使用以下代码:

function whichSubstitute(event) {
  const theChar = event.code;
  if (theChar.startsWith('Key')) {
    return theChar.codePointAt(3);
  }
  if (theChar.startsWith('Digit')) {
    return theChar.codePointAt(5);
  }

  switch (theChar) {
    case "Backspace":
      return 8;
    case "Tab":
      return 9;
    case "Enter":
      return 13;
    case "Alt":
      return 18;
    case "Escape":
      return 27;
    case "Delete":
      return 127;
    case "Minus":
      return 45;
    case "Plus":
      return 43;
    case "Equal":
      return 61;
    case "Delete":
      return 127;
    case "BracketRight":
      return 93;
    case "BracketLeft":
      return 91;
    case "Backslash":
      return 92;
    case "Slash":
      return 47;
    case "Semicolon":
      return 59;
    case "Colon":
      return 58;
    case "Comma":
      return 44;
    case "Period":
      return 46;
    case "Space":
      return 32;
    case "Quote":
      return 34;
    case "Backquote":
      return 39;

    //there are also "Numpad....." variants

    case "Unidentified":
      alert("handle the 'Unidentified' if you want to!");
  }

  /*** there are many other possible character values https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
        but, AFAIK, there are no unicode code points for them, such as:

  switch (theKey) {
    case "AltLeft":
    case "CapsLock":
    case "ControlRight":
    case "Fn":
    case "NumpadDecimal":
    ...

       event.which may output some number for them, but it is not consistent across
       browsers/machines and they may overlap with other code points.

  ***/

  return 0;
}

//test
document.onkeydown = (event) => {
  console.log('whichSubstitute: ' + whichSubstitute(event) + '; event.which: ' + event.which);
}

第二种方法可能没有用,因为相同的物理键可能会根据不同的键盘布局输出不同的unicode字符。用户可能不知道应该按哪个键。

相关:https://www.w3.org/TR/uievents/#keys