Skip to content

Instantly share code, notes, and snippets.

@arnehormann
Last active October 13, 2015 07:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arnehormann/4163916 to your computer and use it in GitHub Desktop.
Save arnehormann/4163916 to your computer and use it in GitHub Desktop.
javascript keycode finder
<!DOCTYPE html>
<html><head><meta charset="utf-8"><!--
Ok, let me be honest here…
DISCLAIMER:
I drew inspiration and a little CSS from the now defunct
http://whatthekeycode.com/
Still, it's different in a lot of ways.
This is the perfect single page app by the way :-)
It really won't tax your network connection once it's loaded,
this file is all there is!
It is fully commented and – even better – narrated.
It uses all of HTML, CSS and Javascript comments.
So if you ever forget the syntax - here's your refresher!
--><title>Javascript Key Expander</title>
<style type="text/css">
/*
Thou shalt please the eye!
*/
body {
font-family: "Museo Sans", "Droid Sans", "Helvetica Neue", sans-serif;
background: #eee
}
#help {
position: fixed;
font-size: 40px;
line-height: 1.5em;
text-align: center;
width: 100%;
left: 0;
line-height: 1.5em;
top: 50%;
margin-top: -100px;
color: #888;
}
#keys {
line-height: 2em;
}
a {
text-decoration: none;
color: #888;
}
/*
Some semantics abuse on my part: I misappropriate "b" tags for keys.
At least in #help and #keys
*/
#help b {
font-size: 30px;
}
#keys b, #help b {
display: inline-block;
font-family: DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace;
line-height: 1em;
padding: 0.3em 0.5em;
margin-bottom: 0.5em;
color: #666;
border: 1px solid #666;
border-radius: 0.3em;
optimize: legibility;
/*
Do yourself a favor and never write stuff like this by hand.
Get aquainted with LESS or SASS or use one of the numerous sites out there.
*/
background: #f7f7f7;
background: -moz-linear-gradient(top, #f7f7f7 0%, #e8e8e8 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f7f7f7), color-stop(100%,#e8e8e8));
background: -webkit-linear-gradient(top, #f7f7f7 0%,#e8e8e8 100%);
background: -o-linear-gradient(top, #f7f7f7 0%,#e8e8e8 100%);
background: -ms-linear-gradient(top, #f7f7f7 0%,#e8e8e8 100%);
background: linear-gradient(to bottom, #f7f7f7 0%,#e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7f7f7', endColorstr='#e8e8e8',GradientType=0 );
-moz-box-shadow: 1px 1px 3px 0px #ccc;
-webkit-box-shadow: 1px 1px 3px 0px #ccc;
box-shadow: 1px 1px 3px 0px #ccc;
}
/*
When hovered or in container with the class "display-keycode",
we show the keycode (which sits snuggly in the title attribute)
*/
#keys.display-keycode b:after, #keys b:hover:after {
content: attr(title);
display: inline-box;
margin-left: 0.5em;
padding: 2px;
font-size: 0.5em;
font-family: sans-serif;
font-weight: normal;
color: #eee;
background-color: #888;
border-radius: 2px;
}
</style>
</head>
<body>
<div id="help">
Enter a key or a key combination.<br>Hover to see the keycode…<br>
or try <b class="key">Ctrl</b> + <b class="key">K</b><!--
Toggles the display by adding a class display-keycode to "#keys".
See ctrlKHandler for how that's done!
--><br>
the source is <span title="Honestly! Just about 400 lines give or take. Well groomed.">short and simple</span> –
have a <a href="view-source:" title="Firefox does this. Chrome doesn't. I didn't test the others">look</a>!</div>
<div id="keys"></div><script>
(function(){
// cache the only DOM node we need
var keys = document.getElementById('keys')
, keyMap =
/*
Here, we have 3 objects mapping from keycodes to strings.
We'll need them soon.
The first one is generic and may be overridden by the later ones.
Stay with me for a while, it will all be clear… Umm…
Scroll ↓, please
*/
{ 8: 'Backspace'
, 9: 'Tab'
, 13: 'Enter'
, 16: 'Shift'
, 17: 'Ctrl'
, 18: 'Alt'
, 19: 'Break'
, 20: 'Caps Lock'
, 27: 'Esc'
, 32: 'Space'
, 33: 'Page ↑'
, 34: 'Page ↓'
, 35: 'End'
, 36: 'Home'
, 37: '←'
, 38: '↑'
, 39: '→'
, 40: '↓'
, 45: 'Insert'
, 46: 'Delete'
, 96: 'Num 0'
, 97: 'Num 1'
, 98: 'Num 2'
, 99: 'Num 3'
, 100: 'Num 4'
, 101: 'Num 5'
, 102: 'Num 6'
, 103: 'Num 7'
, 104: 'Num 8'
, 105: 'Num 9'
, 106: 'Num *'
, 107: 'Num +'
, 109: 'Num -'
, 110: 'Num .'
, 111: 'Num /'
, 112: 'F1'
, 113: 'F2'
, 114: 'F3'
, 115: 'F4'
, 116: 'F5'
, 117: 'F6'
, 118: 'F7'
, 119: 'F8'
, 120: 'F9'
, 121: 'F10'
, 122: 'F11'
, 123: 'F12'
, 144: 'Num Lock'
, 145: 'Scroll Lock'
}
/*
I'm from Germany and using Windows right now.
Here's some mappings specific to Windows.
↓ go on ↓
*/
, keyMapWindows =
{ 91: 'Windows Left'
, 92: 'Windows Right'
, 93: 'Context'
// special keys on multimedia keyboard - still missing Email and whatnot…
, 172: 'WWW Home'
, 173: 'Mute'
, 174: 'Volume down'
, 175: 'Volume up'
, 179: 'Play/Pause'
, 181: 'Media Player'
, 182: 'My Computer'
, 183: 'Calculator'
}
/*
As I said, I'm from Germany.
Our keyboard layouts are different!
There's more.
We use "," as a decimal separator, so that's on our
Num blocks instead of "." (we use "." for thousands *sigh*).
↓ just a little more ↓
*/
, keyMapDE_Win =
{ 110: 'Num ,' // different decimal separator in DE :-(
, 186: 'ü'
, 187: '+'
, 188: ','
, 189: '-'
, 190: '.'
, 191: '#'
, 192: 'ö'
, 219: 'ß'
, 220: '^'
, 221: '´'
, 222: 'ä'
, 226: '<'
}
/*
We get a key but we want its name - when JS doesn't know how to convert it.
That's what this function does. Pass a keycode, get a string.
*/
, keyToString = function(key) {
// Now we need the keyMaps from above.
// Starting from the leftmost one, the key is searched in the maps.
// If it's not found, the result is "undefined" (which is evaluated to false)
// and the next one is checked.
var expandedKey = keyMapDE_Win[key] || keyMapWindows[key] || keyMap[key]
if (expandedKey) {
// We found a long representation and use it
return expandedKey
}
// Use JS to convert the keycode to it's string representation
return String.fromCharCode(key)
}
/*
When we have a keycode, we want to display a nice little key!
That's done this way.
The title attribute contains the keycode, the text its description.
*/
, keyToHTML = function(key) {
return '<b title="' + key + '">' + keyToString(key) + '</b>'
}
/*
Now it's getting slightly more complicated.
This thingy is a fully encapsulated key event stream handler.
There's no changing it from the outside - it just eats key presses.
And it's fed from outside.
Positive numbers indicate a "key pressed" keycode.
Negative numbers indicate that key was released.
We get by with numbers only! Neat!
*/
, streamProcessor = (function() {
var keyBuffer = [] // buffers all keys still pressed
, activeCombination = false // some keys pressed, none released?
// remember: for (k) → positive = pressed; negative = released
return function(k) {
if (k > 0) { // key pressed
if (keyBuffer.indexOf(k) === -1) { // key's not pressed yet
// remember the new keycode
keyBuffer.push(k)
// the "if"s down there control formatting.
// If it's the first key, it should stand alone.
// If it's a new key, we have to prefix a "+"
// If some keys are still pressed but some were released,
// we end the prior ones with a "," and continue
// with the keys pressed so far.
if (keyBuffer.length <= 1) {
// it's the only key
keys.innerHTML = keyToHTML(k)
} else if (activeCombination) {
// some preceding keys
keys.innerHTML += ' + ' + keyToHTML(k)
} else {
// restart combination
var html = ' , ' + keyToHTML(keyBuffer[0])
for (var i = 1; i < keyBuffer.length; i++) {
html += ' + ' + keyToHTML(keyBuffer[i])
}
keys.innerHTML += html
}
// at least some keys are pressed and none were released,
// this is part of a combination
activeCombination = true
}
} else { // key released
var i = keyBuffer.indexOf(-k) // do we know it? It's a key release, so -k
if (i >= 0) { // Yeah, we know it
keyBuffer.splice(i, 1) // and we forget it
activeCombination = false // but it's no longer the same combination
}
}
}
// TODO feature idea:
// Per default, show lower case and
// *optionally* wrap [shift] + [letter] into [LETTER]?
})()
/*
With me so far?
This thingy converts key events into the positive / negative keycodes
and feeds them to a function. The one above, that is.
*/
, streamKeys = function(factor, callback, cancelling) {
// factor should be 1 for "key pressed" and -1 for "key released"
if (cancelling) {
// when cancelling, we event processing to stop after we got it.
// in this case, Ctrl-C, Ctrl-V and some others don't work anymore.
// But the browser won't do funny stuff on combinations.
// By the way, here's an exciting read: http://unixpapa.com/js/key.html
return function(e) {
e = e || window.event
callback(factor * (e.keyCode || e.which))
return false
}
}
// same without "return false"
return function(e) {
e = e || window.event
callback(factor * (e.keyCode || e.which))
}
}
/*
listen for and react to Ctrl + K as hinted in help
*/
, ctrlKInterceptor = function(wrapped){
var last = 0
return function(k) {
if (k === 17) {
last = 17
} else if (last === 17 && k === 75) {
// when first key was "Ctrl" and now we got "k", toggle class
keys.className = keys.className === 'display-keycode'
? ''
: 'display-keycode'
} else {
last = 0
}
wrapped(k)
}
}
/*
some fairy dust for those in the know!
↑↑↓↓←→←→BA - the Konami code.
We listen for it here
*/
, konamiInterceptor = function(wrapped) {
var konamiCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]
, konamiIdx = 0
return function(k) {
if (k >= 0) {
if (k === konamiCode[konamiIdx]) {
konamiIdx++
if (konamiIdx > 1) {
keys.innerHTML += ' - ' + keyToHTML(k)
if (konamiIdx === konamiCode.length) {
// Oh, oh, we could change this to a fading popover!
// with sparkles, rainbows, unicorns, …
alert('Ok, you win!')
}
}
} else {
konamiIdx = 0
}
} else {
if (konamiIdx === konamiCode.length) {
keys.innerHTML = ''
}
if (konamiIdx > 0 && -k !== konamiCode[konamiIdx - 1]) {
konamiIdx = 0
}
}
if (konamiIdx <= 1) {
wrapped(k)
}
}
}
/*
Yo dawg, I wrap wrappers in wrappers so you can wrap while you're wrapping…
*/
, streamHandler = (function() {
var callback = konamiInterceptor(ctrlKInterceptor(streamProcessor))
return function(isKeyPress) {
return streamKeys(isKeyPress ? 1 : -1, callback, true)
}
})()
, onDown = streamHandler(true)
, onUp = streamHandler(false)
/*
Now let's just register them and that's it!
*/
document.body.onkeydown = onDown
document.body.onkeyup = onUp
})()
/*
THE END
I hope you liked it!
*/
</script></body></html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment