web-dev-qa-db-ja.com

イベントの非推奨となったKeyboardEvent.whichプロパティの代替

MDNは _KeyboardEvent.which_ は非推奨であると述べています。非推奨ではないバージョンをどのように置き換えることができますか?

たとえば、私は次のとおりです。

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

event.key.charCodeAt()は_event.which_に置き換えることができると思いましたが、これは次のようなキーでは機能しません ALT、 CTRL または ENTER、および_event.key.length === 1_の場合にのみ機能します。

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

要約すると、_event.which != event.code_および_event.which != event.key_なので、単純に_event.key_を使用することはできません。

_event.which_に代わるものはありますか? ALT、 CTRL または ENTER

10
flen

TL; DR:これらは従うべきルールです:

  • ユーザーからテキスト入力を取得するときは、keypressイベントを_e.key_とともに使用します
  • ショートカットやその他の組み合わせの場合、組み込みの方法はkeydown/keyupを使用してさまざまな修飾キーを確認することです。コードを検出する必要がある場合は、ステートマシンを構築する必要があります。

バックグラウンド

キーボード入力は2つのフェーズに分割されます。つまり、押されている物理キーを追跡するキーダウン/キーアップペアと、複数のキーシーケンスを組み合わせて文字を計算する合成文字です。

「テキスト」を取得する

オペレーティングシステムが構成されたシーケンスをどのように考えているかを知りたい場合は、 KeyboardEvent.key を使用する必要があります。

サンプルコード:

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

ほとんどの場合、これを実行する理由は、多くの言語が複数のキーを押して文字を構成するためです。 US-101キーボードで理解するのが最も簡単なのは、shiftを押すだけの場合と比較して、aキー+ Aaを押すことです。 altgr デッドキーを使用するロシア語などの言語では、これは特に重要になります。

私が作ろうとしている点は、これらすべての作業を自分で行うことです。キーシーケンスを検出して正しいテキスト出力を決定することは難しい問題です。理由は、オペレーティングシステムの仕事です。

現在、古いブラウザでは、古いサポートが不足しているため、_e.key_を使用したくない場合があります。その後、whichなどの標準的でないアプローチにフォールバックできます。

将来のある時点で、ブラウザによってキープレスが削除される可能性があります。 beforeinput イベントが置き換えになるはずです。ただし、このイベントはChromeでのみサポートされているため、簡潔にするためにここでは省略しています。

キーストロークを取得する

次に、テキストを追跡するのではなく、キーシーケンスを追跡するとします。これは、ゲームや_ctrl-c_などを聞くためのものです。この場合、正しいことは、keydown/keyupevents を聞くことです。 =。修飾キーの場合、イベントのctrlKeyshiftKey、およびmetaKeyプロパティをリッスンするだけです。下記参照:

_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と表示されますが、OSはそれをoに変換します。これはdvorakだからです。この場合のコードは、常にスキャンコード(物理キーが押されている)を参照しますが、キーは、「テキスト」が何であるかを理解するためのオペレーティングシステムのベストエフォートに対応します。特に他の言語では、これが常に可能であるとは限りません。繰り返しになりますが、keypressのキーを使用することが正しい方法です。

サードパーティライブラリ

これが特に簡単に聞こえない場合は、そうではないからです。私がこれを最後に見たとき、私は mousetrap ライブラリに出くわしましたが、私が見つけた問題のいくつかを考えると、それをお勧めするかどうかはわかりません。ただし、主要なコードを追跡するステートマシンを構築する例は示しています。

補遺

これは、キーストロークを食べたい場合にkeydown/keyupを追跡する必要がある理由でもあります。 ctrl + cの「テキスト」がないため、適切なキーを押すことができず、ブラウザがネイティブに処理します。独自の動作を実行したい場合は、キーダウン自体でe.preventDefault()を実行する必要があります。 (copyのようなフォローアップイベントの一部もキャンセルできますが、それは普遍的には当てはまりません)

入力フィールド(またはcontenteditable div)に事後的に挿入されたキーも追跡する必要がある場合は、 input イベントを参照してください。

履歴:2019年8月に更新され、キー入力を変更->入力前

16
AnilRedshift

他の回答が指摘したように、_event.which_には1つの主な問題があります。それは、異なるブラウザーまたはコンピューターに対して同じ数を返さないことです(これが非推奨の理由かもしれません)。したがって、ユーザーごとに異なる数値を出力するため、完全な代替はありません。

そのため、代わりのものを作成しようとする際の主な問題(名前をfunction whichSubstitute(event)としましょう)は、 Meta そして Shift たとえば、キーの1つが押されたときにwhichSubstituteが取得する必要がある一意の番号はありません OSによって異なります

そのことを念頭に置いて、ユーザー入力用のユニコードコードポイントを取得する方法は2つあります。

  1. ユーザーが入力した文字のUnicode値を取得します(例:_ü_、'ü'.codePointAt(0)になります)。
  2. キーボードで押された物理キーに対応する文字の数値を取得します。これは、テキストフィールドに入力されたものとは異なる場合があります。 AnilRedShiftが述べたように、キーボードレイアウトは、キーボードのそのキーからの「自然な」出力を、キーが s oを出力する場合があります。この場合、最初のアプローチを使用する場合のように、_'o'_(つまり、実際に出力されたもの)の値を取得する代わりに、's'.codePointAt(0)を取得します。 これの詳細はMDNから

たとえば、返されるコードは "KeyQ"はQWERTYレイアウトキーボードの "q"キー用ですが、同じコード値はDvorakキーボードの「_'_」キーとAZERTYキーボードの「a」キー。これにより、コードの値を使用して、キーの名前を判別することができなくなります。予想されるキーボードレイアウトを使用します。

つまり、アプローチ番号1は_ü_のUnicodeコードポイントを取得し、アプローチ番号2は SHIFT、 6 そして U (以来 SHIFT+6+U == _ü_)。

この回答では、String.prototype.codePointAt()ではなくString.prototype.charCodeAt()を使用します。 違いはここで詳しく説明されています 。その理由は、.codePointAt(0)を使用してユニコード番号全体を取得できるのに対し、.charCodeAt(0)は、UTF-16エンコードされたコードポイントを完成するための.codePointAt(1)を欠いているためです。

アプローチnumber 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)
}_

もちろん、これはone unique押されたキーの一貫した番号のみを取得する問題を解決しませんnicodeコードポイントがない場合(の場合のように Meta)。このようなキーは、プログラマが自分のニーズに応じて処理する必要があります。

アプローチnumber 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文字を出力する可能性があるため、この2番目のアプローチは役に立たない場合があります。ユーザーはどのキーを押すべきかわからないかもしれません。

関連: https://www.w3.org/TR/uievents/#keys

2
flen

仕様から:

which unsigned long型、読み取り専用

押されたキーに関連付けられた未変更の識別子を示す、システムおよび実装に依存する数値コードを保持します。ほとんどの場合、値はkeyCodeと同じです

keyCode unsigned long、読み取り専用のタイプ

keyCodeは、押されたキーに関連付けられた未変更の識別子を示す、システムおよび実装に依存する数値コードを保持します。 KeyboardEvent.key属性とは異なり、可能な値のセットはこの仕様では規範的に定義されていません。通常、これらのkeyCodeの値は、10進コードポイントをASCII [RFC20] [US-ASCII]またはWindows 1252 [WIN1252])で表す必要がありますが、別の適切な文字セットから取得できます。キーを識別できない場合は、キー値「0」を使用します。

KeyCodeの値を決定する方法の詳細については、レガシーキーモデルを参照してください。

(リンクを省略しています)

したがって、仕様と互換性のあるバージョンを作成するのは非常に簡単です。最も簡単なバージョンは、各キーに対して0を返すだけです。

もう少し複雑なバージョンはevent.keyを受け取り、それを取得しますASCII数値です。コントロールキー(altKeyctrlKeyshiftKey)。

つまり、現状ではwhichの動作はシステムとブラウザで異なります。非推奨ではないイベント情報を使用して、これらの違いを取り除き、より将来の証明となる、より堅牢なバージョンを作成できます。

主要なブラウザでwhichを実装すると、バージョンの動作を確認できます。 Edgeケースを使用していない場合、バージョンはシンプルで互換性があります。

2
EECOLOR