What preferred way to reconcile keyCode/charCode across browsers? - javascript

Based on the properties of the keydown event I would like to ascertain what the current charCode are?
Example.
For the keydown event when the NumPad0, D0, and Colon key is pressed I would like to know what the associated charcode is. Currently I have a map that contains the charcode associated with that keyCode or use the current if charCode is not specified.
keyCode = {
Colon: 186,
D0: 48,
NumPad0: 96,
};
charCodes = {
186: 59,
96: 48,
};
shiftCharCodes = {
186: 58,
48: 41
};
Also in certain cases the keyCodes are different accross browsers?
Example.
The keydown event has different keyCode values across browsers.
Colon Key (:/;)
- keyCode is 59 on firefox
- keyCode is 186 on IE/safari
For more information
http://www.quirksmode.org/js/keys.html

If you're interested in the character code associated with a keypress, you're going to get nowhere with the keydown event. The keypress event is the only place this information is available, and is not too problematic for most printable keys.
document.onkeypress = function(evt) {
evt = evt || window.event;
var charCode = evt.which || evt.keyCode;
alert("Character: " + String.fromCharCode(charCode));
};
There are differences between browser behaviour around "special" non-printable keypresses such as function keys. For more information I consider this article by Jan Wolter to be the definitive reference for JavaScript key handling.

Although I generally loath answers like the one I am about to give, I feel it is appropriate:
This is a perfect use case for a library like jQuery, Prototype, Dojo or MooTools.
You don't want to burden yourself with this work, it has already been done.

Related

How do I detect any change to a textarea?

I currently have bound my textarea to a couple of events which seems to work. However, the problem is that the events overlap and fire several times, which in turn reduces performance by a factor of too much.
What I want to do is pretty much catch any change to the textarea: clicking, paste, keyup, keydown, right click context menu editing (right click, cut/delete/paste), drag and drop, etc. This has to work cross-browser and at least down to IE8. The events have to fire when you move the caret around in the textarea using arrowkeys or similar (I handle changes based on caret position, among other things).
I can't use any major delays. As soon as you do something with the textarea, the events have to fire and execute whatever code I have there immediately.
I am currently using jQuery to bind the event, but I am fine with a pure javascript solution as long as it works cross browser and does what I want.
Here's the code I currently use:
var deadKeycodes = [16, 17, 18, 19, 20,
27, 33, 34, 35, 36,
38, 40, 44, //37 = left arrow and 39 = right arrow removed, it needs to trigger on those
45, 112, 113, 114, 115,
116, 117, 118, 119, 120,
121, 122, 123, 144, 145];
$(original).bind('propertychange keyup keydown input click', function(e) {
if (!Array.prototype.indexOf || deadKeycodes.indexOf(e.keyCode) == -1) { // prevent execution when pressing a 'dead' key
//do stuff here
}
});
If anything is unclear just ask and I'll clarify it for you :)
I don't think you can really "solve" the problem in the sense of stopping multiple events from firing. Keyup and keydown really are different events and happen at different times. If you want to respond to both (which you probably do, since keying down and keying up will both potentially change the textarea), both events need to be included. However, most of the time they will fire almost simultaneously (and many times in a row), which as you point out can pose a performance problem.
Instead, you should probably consider firing a throttled or debounced callback. A throttled callback only will fire once every n milliseconds (good for functions that might get called too much). A debounced callback will only fire after a stream of events is done; after n milliseconds have elapsed since the last callback.
You can easily accomplish this using underscore's debounce and throttle functions.
Something like:
debouncedFn = _.debounce(function(e) {
if (!Array.prototype.indexOf || deadKeycodes.indexOf(e.keyCode) == -1) { // prevent execution when pressing a 'dead' key
//do stuff here
}
}, 100);
$(original).bind('propertychange keyup keydown input click', debouncedFn);
Your original is overkill. All you need are the input and propertychange events.
2016 Update
The originally linked page has now disappeared. Here's a slightly suboptimal snapshot of it:
http://web.archive.org/web/20140810185102/http://whattheheadsaid.com/2010/09/effectively-detecting-user-input-in-javascript
Here's an answer demonstrating using the input event and falling back to propertychange in IE <= 8:
Catch only keypresses that change input?
To prevent the event overlap, you could store the time of the last call. Before you execute an event's callback, you test if enough time has passed to make sense to fire again.
$(original).bind('propertychange keyup keydown input click', (function () {
var lastCallTime = 0;
return function (e) {
if (Date.now() - lastCallTime < 50) { return; } // Too little time has passed--exit
if (!Array.prototype.indexOf || deadKeycodes.indexOf(e.keyCode) == -1) {
lastCallTime = Date.now();
// your code...
}
};
}()));
This seems to solve it in IE7-9 and Chrome, haven't tested the rest. Only one console.log happens per change regardless of what the change was. If there were no changes, nothing is logged: http://jsfiddle.net/SJN6J/2/
var timer;
$("textarea").on("keydown paste cut", function(){
clearTimeout(timer);
var origvalue = this.value, self = this;
timer = setTimeout(function(){
if ( origvalue !== self.value ) {
console.log("it changed!");
// do stuff because content changed
}
},0);
});
Note: my IE7-8 testing was with IE9 changing browser mode, so you may want to do real IE7-8 testing.

Overlapping key events

I am working on a little HTML/JavaScript/CSS3 project for fun. I'm basically trying to make a wheel that rolls around in the browser window. To control the wheel I'm using keyup and keydown events for the cursor keys (left and right turn the wheel and up and down roll it forward or backward).
I've got it working pretty well so far, but there are two major glitches. Say I want to roll the wheel forward and without stopping I want to turn it a little to the right, then I would keep the up key pressed and press the right cursor key. When I do this there's a pause in the movement before it registers both events and keeps rolling.
That's one of the problems, the main problem is that, once I've performed the previous action and then wheel is at a desirable angle, if I let go of the right cursor key the browser registers both keys as released and the wheel comes to a stand still. Here is a jsFiddle of what it looks like: http://jsfiddle.net/UKqwu/1/. I know the code is a mess but it's a work in progress/learning experience and I've only been programming for a month or so.
Anyways thanks for any help. It only works in Chrome at the moment as far is I know. Haven't really been bothered fixing compatibility issues at this stage.
So, what is happening is essentially a limitation built in by your operating system, but there is a simple work-around. First I'll explain the limitation, and then the work-around.
If you were to (in a text box) hold down the "j" button, first one "j" would appear, and then after a short delay many "j"s would appear "jjjjjjjjjjjjjjjjjjjj"
This is the same problem your experiencing. The event fires once, waits for a moment, and then fires many more times.
The solution, however is simple. Instead of having your wheel move when the events are fired... have it update constantly, and separately keep track of what keys are up or down.
The Key Handler would look something like this...
function KeyHandler() {
this.left = false;
this.right= false;
...
function onKeyDown(e) {
if (e.keyCode == 37) {
this.left = true;
}
...
}
function onKeyUp(e) {
if (e.keyCode == 37) {
this.left = false;
}
...
}
}
(you'd attach the key handler to the body or whatever element you wish)
Your wheel would have an update function that looked like...
wheel.update = function() {
// the wheel is turning left
if (wheel.keyhandler.left) {
// make the appropriate adjustments
}
}
and then the game would have something like this...
wheel = new Wheel;
setInterval(function() {
wheel.update();
},100);
That way your wheel will always be updating based on the current state of the keys, and you wont have to rely on the limitations of events that are firing. :)
Here's a snippet of a simple game I once wrote
//key codes
var KEY_LEFT = 37;
var KEY_RIGHT = 39;
var KEY_A = 65;
var KEY_D = 68;
var KEY_SPACE = 32;
var keys = {};
function onKeyDown(e)
{
e = e || window.event;
var key = window.event.keyCode || e.which; // ie
keys[key] = true;
}
function onKeyUp(e)
{
var key = window.event.keyCode || e.which; // ie
delete keys[key];
}
This keeps track of all current key states. Then your game "tick" is on a setTimeout() rather than moving on key events and checks for appropriate keys.
function gameTick()
{
// update paddle angles
if (keys[KEY_LEFT])
{
// move left
}
if (keys[KEY_RIGHT])
{
// move right
}
}
Here's the game;
the problem you are facing is because your code is meant to detect single key press while your game needs 2 key press detection.
put this line in loop over size of e. that will set all pressed keys as 1. currently its detecting only one key press and processing for one at a time.
keys[e.keyCode] = 1;
check out this thread and u might get what you need. though its jquery, it might help conceptually. m also working on this with js... if anything new comes up will share...
Detect multiple keys on single keypress event in jQuery
your code and concept is cool if you are really one month old in programming.

Javascript/jQuery: Unable to get proper keyCode when capsLock is ON

I'm trying to determine the proper key/char code in javascript when a key is pressed.
It seems that when the CapsLock key is on, lowercase letters are not detectable.
Try the combinations:
1. a = a (97) shift:false
2. Shift+A = A (65) shift:true
3. Capslock,A = A (65) shift:false
4. Capslock, Shift+A = A (65) shift:true -- this should be 'a'
Cases 2 & 4 are indistinguishable.
A simple fiddle to illustrate the problem.
http://jsfiddle.net/sramam/pet5G/3/
OUTPUT:
keypress 'a' shift:false charCode97 keyCode:97 which:97
keypress 'A' shift:true charCode65 keyCode:65 which:65
(CAPSLOCK ON)
keypress 'A' shift:false charCode65 keyCode:65 which:65
keypress 'A' shift:true charCode65 keyCode:65 which:65
I only have a MacPro(Lion) to try this on.
Is it even possible to detect character to be rendered correctly?
The keypress detection is in fact "correct." The issue you're running into is that on OS X Lion, if you enable caps lock and press shift, it IGNORES caps lock. On Windows, shift + caps lock will return lower case letters. On your Mac, it will return upper case letters. This is not a matter of how the browser interprets a keypress, it's what the operating system registers.
Try typing in ANY application, i.e. Terminal, and you should see what I mean.
This may be the case for other Mac OS versions, I tested this on my MacBook Air w/ OS X Lion.
The keypress code is correct on windows, returning the correct key codes for the letter as it would be printed on keypress.
CAPS LOCK ON:
PRINTED
CHARACTER keyup/down keypress Modifiers
z 90 122 +Shift
a 65 97 +Shift
Z 90 90
A 65 65
caps lock off:
Z 90 90 +Shift
A 65 65 +Shift
z 90 122
a 65 97
The keydown, keyup, and keypress events are supported by all browsers, but there are some interoperability problems because the values of the keyCode property of the event object have never been standardized.
// The legacy keyCode property of the keydown event object is not standardized
// But the following values seem to work for most browsers and OSes.
Keymap.keyCodeToKeyName = {
// Keys with words or arrows on them
8:"Backspace", 9:"Tab", 13:"Enter", 16:"Shift", 17:"Control", 18:"Alt",
19:"Pause", 20:"CapsLock", 27:"Esc", 32:"Spacebar", 33:"PageUp",
34:"PageDown", 35:"End", 36:"Home", 37:"Left", 38:"Up", 39:"Right", 40:"Down", 45:"Insert", 46:"Del",
// Number keys on main keyboard (not keypad)
48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",
// Letter keys. Note that we don't distinguish upper and lower case
65:"A", 66:"B", 67:"C", 68:"D", 69:"E", 70:"F", 71:"G", 72:"H", 73:"I", 74:"J", 75:"K", 76:"L", ` 77:"M", 78:"N", 79:"O", 80:"P", 81:"Q", 82:"R", 83:"S", 84:"T", 85:"U", 86:"V", 87:"W", 88:"X", 89:"Y", 90:"Z",`
// Keypad numbers and punctuation keys. (Opera does not support these.)
96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9", 106:"Multiply", 107:"Add", 109:"Subtract", 110:"Decimal", 111:"Divide",
// Function keys
112:"F1", 113:"F2", 114:"F3", 115:"F4", 116:"F5", 117:"F6", 118:"F7", 119:"F8", 120:"F9", 121:"F10", 122:"F11", 123:"F12", 124:"F13", 125:"F14", 126:"F15", 127:"F16", 128:"F17", 129:"F18", 130:"F19", 131:"F20", 132:"F21", 133:"F22", 134:"F23", 135:"F24",
// Punctuation keys that don't require holding down Shift
// Hyphen is nonportable: FF returns same code as Subtract
59:";", 61:"=", 186:";", 187:"=", // Firefox and Opera return 59,61 188:",", 190:".", 191:"/", 192:"`", 219:"[", 220:"\\", 221:"]", 222:"'"
};

How do you track Caps Lock users with Google Analytics?

I enjoy working with Google Analytics and the ways that I am able to slice information about our visitors. We use Customer Variables to track information about who and how are users interact with our site. Staying true to the goal of Analytics, we are always looking for ways to improve and optimize our website.
Currently we are in a phase of development where we can make choices about how we want to store and present product information. One of the questions that came up was whether or not to show product information in all caps or not. Working with our users for the last few years, it seems that much of our traffic comes from visitors who do have caps lock on. So it got us thinking, could we track our caps lock users with a customer variable to that we can make a more informed determination about how to present the information?
Check out this sample I slapped together: http://jsfiddle.net/shanabus/Za4kL/
Our site basically represents a standard e-commerce site. There are a few different text boxes and that allow you to search for part numbers and throughout the order process there are a few places where users can type text. Would you bind the caps lock test to all text boxes or just the common ones? Is there a performance hit if I bind the keypress listener to all text boxes on the site or is it negligible? Is there a better way to implement this?
I imagine instead of showing/hiding a div I would instead set the custom var:
_gaq.push('_setCustomVar', 5, 'capslock', 'true', 3);
Thanks for any thoughts and consideration on this seemingly trivial topic.
I'd bind the event globally, and use the following code:
var CAPS_ON = null;
$(window).keypress(function(ev) {
var charCode = ev.which; //jQuery normalizes ev.charCode to ev.which
// Lowercase chars
if (charCode >= 97 && charCode <= 122) {
CAPS_ON = ev.shiftKey; // Caps are off if SHIFT is not pressed
} else if (charCode >= 65 && charCode <= 90) {
CAPS_ON = !ev.shiftKey;
}
});
This creates a variable CAPS_ON, which can be used throughout the page.
Further explanation on the code:
The event has to be bound to the keypress event, because it's the only key event which discerns lowercase/uppercase characters.
The shiftKey property has to be checked, because it inverts the CAPS LOCK feature.

Help debugging window.event.screenX

I'm using firebug and the latest FF to debug this bit of javascript. When line #30 is hit in the debugger the screen tries to refresh with message
"To display this page, firefox must
send information that will repeat any
action (such as a search or order
confirmation) that was performed
earlier.
Javascript with line #'s Called via OnClick()
function EnterComment(divName, /*...couple other params*/) {
25
26 thatDiv = divName;
27 //...comment
28
29 // Mouse Position with offset
30 var x = window.event.screenX + 10;
31 var y = document.body.scrollTop + 20;
This doesn't work in IE either. Is there any way to get an exception from Firebug? Any other tips to fix this?
Firefox doesn't have a global "event" object at all. Instead, a reference to an essentially similar object is passed in to event handlers by the runtime system.
I can't tell what sort of code is that fragment you posted. As an example, if you're binding event handlers with the basic DOM 0 attributes, you can do this:
<a onclick='yourhandler(event)'>hi</a>
and then:
function yourhandler(event) {
event = event || window.event;
var X = event.screenX;
// ...
}
If, as you mention in a comment, you were to use jQuery to bind handlers, then it's even easier because it's the same in all browsers:
$(function() {
$('#myAnchor').click(function(event) {
var X = event.pageX;
});
});

Categories