Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2009-06-30

SafariとChromeではkeyIdentifierが使えるので素晴らしすぎます

20:47

keyIdentifier は DOM3 Events から削除され、key value というものに置き換わりました。詳しくはこちらを参考に。



以下の内容は古いです。絶対に参考にしないでください。

DOM3 の keyIdentifier という仕様は、現在これほどまでにブラウザ毎、OS 毎でぐちゃぐちゃになっているキーボードイベントを統一するためのもの。


実は Safari と Chrome では既に実装していたそうな。(もっとずっと先の話かと思ってた)

使い方は、keydown か keyup の EventListener において、event.keyIdentifier を見る。

Unicode に対応するキーに関しては U+0009 といった Unicode のコードポイントで表され、それ以外のキーは "Enter" とか "Left" とかになる。


現在のところ、Mac OS X 上で、Chromium の最新ビルドと Safari 4 ではキーイベントに若干の違いがある。Safari では Shift や Control というモディファイアキーもイベントが出るのに、Chromium では出ないとか、Chromium でイベントをキャンセルできる条件が結構ややこしいといった具合に。

そのへんのバグはまだ Alpha なので無視すると、キーとモディファイアに一致するキーがだいたいこんな感じで得られる。(p 関数が定義されているとする)

  window.addEventListener('keydown',function(e){
    p(get_key(e));
    e.preventDefault();
  },false)

  var keyId = {
    "U+0008" : "BackSpace",
    "U+0009" : "Tab",
    "U+0018" : "Cancel",
    "U+001B" : "Esc",
    "U+0020" : "Space",
    "U+0021" : "!",
    "U+0022" : "\"",
    "U+0023" : "#",
    "U+0024" : "$",
    "U+0026" : "&",
    "U+0027" : "'",
    "U+0028" : "(",
    "U+0029" : ")",
    "U+002A" : "*",
    "U+002B" : "+",
    "U+002C" : ",",
    "U+002D" : "-",
    "U+002E" : ".",
    "U+002F" : "/",
    "U+0030" : "0",
    "U+0031" : "1",
    "U+0032" : "2",
    "U+0033" : "3",
    "U+0034" : "4",
    "U+0035" : "5",
    "U+0036" : "6",
    "U+0037" : "7",
    "U+0038" : "8",
    "U+0039" : "9",
    "U+003A" : ":",
    "U+003B" : ";",
    "U+003C" : "<",
    "U+003D" : "=",
    "U+003E" : ">",
    "U+003F" : "?",
    "U+0040" : "@",
    "U+0041" : "a",
    "U+0042" : "b",
    "U+0043" : "c",
    "U+0044" : "d",
    "U+0045" : "e",
    "U+0046" : "f",
    "U+0047" : "g",
    "U+0048" : "h",
    "U+0049" : "i",
    "U+004A" : "j",
    "U+004B" : "k",
    "U+004C" : "l",
    "U+004D" : "m",
    "U+004E" : "n",
    "U+004F" : "o",
    "U+0050" : "p",
    "U+0051" : "q",
    "U+0052" : "r",
    "U+0053" : "s",
    "U+0054" : "t",
    "U+0055" : "u",
    "U+0056" : "v",
    "U+0057" : "w",
    "U+0058" : "x",
    "U+0059" : "y",
    "U+005A" : "z",
    "U+005B" : "[",
    "U+005C" : "\\",
    "U+005D" : "]",
    "U+005E" : "^",
    "U+005F" : "_",
    "U+0060" : "`",
    "U+007B" : "{",
    "U+007C" : "|",
    "U+007D" : "}",
    "U+007F" : "Delete",
    "U+00A1" : "¡",
    "U+0300" : "CombGrave",
    "U+0300" : "CombAcute",
    "U+0302" : "CombCircum",
    "U+0303" : "CombTilde",
    "U+0304" : "CombMacron",
    "U+0306" : "CombBreve",
    "U+0307" : "CombDot",
    "U+0308" : "CombDiaer",
    "U+030A" : "CombRing",
    "U+030B" : "CombDblAcute",
    "U+030C" : "CombCaron",
    "U+0327" : "CombCedilla",
    "U+0328" : "CombOgonek",
    "U+0345" : "CombYpogeg",
    "U+20AC" : "€",
    "U+3099" : "CombVoice",
    "U+309A" : "CombSVoice",
  }
  function get_key(evt){
    var key = keyId[evt.keyIdentifier] || evt.keyIdentifier,
    ctrl = evt.ctrlKey ? 'C-' : '',
    meta = (evt.metaKey || evt.altKey) ? 'M-' : '',
    shift = evt.shiftKey ? 'S-' : '';
    if (/^(Meta|Shift|Control|Alt)$/.test(key)) return key; // safari only
    if (evt.shiftKey){
      if (/^[a-z]$/.test(key)) 
        return ctrl+meta+key.toUpperCase();
      if (/^(Enter|Space|BackSpace|Tab|Esc|Home|End|Left|Right|Up|Down|PageUp|PageDown|F(\d\d?))$/.test(key)) 
        return ctrl+meta+shift+key;
    }
    return ctrl+meta+key;
  }

例えば

i
C-j
C-Space
M-Enter
C-u
C-U
C-M-d
C-M-D
C-S-BackSpace
M-S-Enter

など。

U みたいに、Shift されているかを気にしなくてもいいものは S- を付けず、Enter のように、Shift があるかどうか判断できないものについては付けることにした。


どうやら落とし穴があるっぽい

os0x さんの os0x / ChromeKeyconfig のソースを読んでて知ったのだけど、どうやら Windows では上の通りに keyIdentifier が取得できないっぽい。

そのソースから抜粋。

var winkeys = {
	"U+00BC":",",
	"U+00BE":".",
	"U+00BF":"/",
	"U+00E2":"\\",
	"U+00BB":";",
	"U+00BA":":",
	"U+00DD":"]",
	"U+00C0":"@",
	"U+00DB":"[",
	"U+00BD":"-",
	"U+00DE":"^",
	"U+00DC":"\\"
};
var shiftWinkeys = {
	"U+00BC":"<",
	"U+00BE":">",
	"U+00BF":"?",
	"U+00E2":"_",
	"U+00BB":"+",
	"U+00BA":"*",
	"U+00DD":"}",
	"U+00C0":"`",
	"U+00DB":"{",
	"U+00BD":"=",
	"U+00DE":"~",
	"U+00DC":"|",
	"U+0031":"!",
	"U+0032":"\"",
	"U+0033":"#",
	"U+0034":"$",
	"U+0035":"%",
	"U+0036":"&",
	"U+0037":"'",
	"U+0038":"(",
	"U+0039":")"
};

マジキモいんですけどー…

本来 keyIdentifier は Shift が押されているかどうかなんて気にしなくても使えるように Unicode またはキー名で識別していたはずなのに。

ただの "U+00BC" だったら "," で、shiftKey が true だったら "<" になるなんて、identifier でもなんでもないじゃん。

めっちゃキー配列依存。"<" を入力するのに Shift を押さなくてもいいキーボードはどうするんだ。

Safari は知らないけど、Chrome の keyIdentifier は残念すぎる、と言っても過言ではなさそう。


仕様もちょっと変更してた

一番上のリンクが消えてて、仕様はこちらに移動してた。


Note: This section is normative.

The list of key identifiers contained in this appendix is not exhaustive and input devices may have to define their own key identifiers. Here is a algorithm to determine which key identifier to use:

  1. Consider the primary function of the key (i.e., without modifiers), taking into consideration the keyboard layout mapping in use, to determine if a corresponding Unicode character exists from which a key identifier may be derived. If multiple Unicode characters exist which correspond to the primary function of the key, the Unicode character with the lowest codepoint must be used.
    1. If the primary function of the key is to generate a character, and that character is in one of the Unicode character categories, then the key identifier shall be a string consisting of just that character. If the primary function of the key is to generate a character in class Ll for which there exists an equivalent, single character in class Lu, the uppercase character should be used instead.
      Is this necessary? Why can't we simply allow lowercase letters as well? If a script author wishes to do a comparison, they can cast the output to upper or lower case as needed. See ISSUE-23.
    2. If the primary function of the key is to generate a character that is not in one of the above general categories, or if the primary function of the key is a function for which there exists a corresponding Unicode character that is not in one of the above general categories, then:
      1. If there exists an appropriate key identifier in the key identifiers set, and that key identifier does not have a Unicode codepoint, that key identifier must be used.
      2. If there exists an appropriate key identifier in the key identifiers set, and that key identifier has a Unicode codepoint, and that key identifier is in one of the Unicode character categories, then the character value itself for that key identifier must be used.
      3. If there is no appropriate key identifier in the key identifiers set, then the key identifier is a string beginning with "U+" and followed by the Unicode codepoint of the character in hexadecimal, using at least four digits. Leading zeroes must be omitted unless they are required to make the codepoint use at least four digits.
  2. For keys with no corresponding Unicode character, a key identifier can be devised. The key identifier should be as human friendly as possible and must not contain whitespace. The identifier must be composed only of characters in the ranges U+0030..U+0039, U+0041..U+005A, or U+0061..U+007A, and must begin with a character in the range U+0041..U+005A.
    Why should we not allow names in other ranges (Chinese, for example)?

Document Object Model (DOM) Level 3 Events Specification
 |