2

What I'm trying to do is put key codes in an array, to do some interesting stuff with later on. So, I capture the keystrokes, get the caret position and put the key code in the array (with some help from MooTools):

var keyArray = [];
$('form').addEvent('keyup', function(event) {
    var pos = document.activeElement.getCaretPosition();

    keyArray[pos] = event.code;
});

Generally speaking, this works great. However, I noticed some undefined values in my array when showing the complete array in my console. Exploring this further, I found out that when typing quickly, the caret position seems to lose track, or being quick/slow to respond. I've made a jsfiddle to demonstrate this: http://jsfiddle.net/HQVR8/1/

If you type quickly in this example, you'll see a caret position sequence like

- 1 - 2 - 3 - 5 - 6 - 6.

But when typing slowly, it's

- 1 - 2 - 3 - 4 - 5 - 6. 

Of course, the trouble now when typing quickly is that I have a undefined value in my array and I overwrite one array item. So the result is different.

My question is if I can somehow let the caret position keep track. I've tried using the 'native' selectionStart instead, but results are the same. I also tried capturing the caret position in a keydown event and putting it in an array in the keyup event. No difference. I'm wondering if using little pauses (ie. forcing the user to type slower) might solve it, but this feels like a hack and I would prefer a 'proper' solution. Hopefully, there is one.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
kasimir
  • 1,506
  • 1
  • 20
  • 26
  • Why are you doing this? What use are the key codes? – Tim Down May 09 '12 at 08:29
  • I add up all the key codes and use this number as extra security measure. The point is I want to be sure people are typing (in this case a password) so I capture the key strokes (omitting function keys etc.). The sum should be the same always, while it does not expose the actual password (because other letters could add up the same). – kasimir May 09 '12 at 09:46
  • Right. Well, you've got other problems then, because the user could use arrow keys or mouse in your password box to edit the password by inserting and deleting characters at any location. I think this is a non-starter, to be honest. – Tim Down May 09 '12 at 09:57
  • This is not a problem, since the location of the inserted character is saved. Mousing, selecting, arrow keys don't make a difference. – kasimir May 09 '12 at 11:42

3 Answers3

0

you are basically making a mess by using an array instead of an object.

array indexes are dodgy and can create sparse arrays if you are not careful. for instance:

var foo = [];
foo[0] = "one!"; // foo.length = 1;
foo[2] = "two!"; // foo.length = 3, foo[1] = undefined

so if you type too fast and skip the return value, it is probably doing just that. also, pasting etc can push caret further down...

you can probably use an object though there are no guarantees on the order of keys in vs keys out in all browsers - particularly webkit, who tend to reorder and put numeric keys at top of object key loops... but if you prefix the key like "pos" + caretIndex, you ought to get FIFO

one way to solve your need to extract the actual codes w/o the undefined is via Array.filter.

eg.

retArray = Array.filter(retArray, function(el) {
    return el !== undefined;
});

With an object, you can just do:

console.log(Object.values(foo));
Dimitar Christoff
  • 26,147
  • 8
  • 50
  • 69
  • This solves the `undefined` problem, but I don't see how it solves the index (caret) being incorrect - or, not where you thought it was. Actually, I had an object in an earlier version and couldn't get that to work with `sum()`, so I switched to the array. However, I think I solved it by using `keydown` instead of `keyup`. I'll post my answer here. – kasimir May 09 '12 at 13:35
  • you should just disallow paste instead of doing all this - you can do so easily by setting paste as a native event and then simply adding an onPaste handler that stops the event. – Dimitar Christoff May 09 '12 at 13:53
  • With an array, couldn't you just do `arr.clean()`? My goal is not to just stop pasting, it's also to generate an extra security code and give some visual feedback. – kasimir May 10 '12 at 14:00
  • well the Array.filter cleans but this re-indexes the array so the indexes lose their meaning as links to caret. you may as well walk the string instead on keyup and re-base. for feedback on how well they are doing, consider this: https://github.com/DimitarChristoff/StrongPass - you can either just use the events and tweak the regexes / pass levels or let it output, dependent on your needs. here's an example fiddle with plain text warnings. http://jsfiddle.net/dimitar/nZn6A/. you can generate security code based upon a hash of the string anyway. – Dimitar Christoff May 10 '12 at 15:16
  • http://mootools.net/forge/p/moocrypt - moocrypt (updated) can allow you to do fancy stuff for extra security. – Dimitar Christoff May 10 '12 at 15:21
  • Thanks for the link. The stuff with saving key codes (which this Q is about) is for logging in, for a bit extra security, but also to give visual feedback (much like http://mattt.github.com/Chroma-Hash/). But I still need something for determining password strength when creating a password. I'm going to try your script for that. – kasimir May 11 '12 at 09:16
0

After some more tests, it appears to be a behavior specific to keyup. When I use keydown I do get a consistent sequence: http://jsfiddle.net/HQVR8/3/

One disadvantage is that keydown is a step behind keyup when you're doing the 'value collecting' I'm doing, but in my setting, this is only a minor issue.

The difference in behavior appears odd to me: when you press four keys at a time, the keyup displays the same caret position for all of them, while keydown is showing four separate caret positions. This seems odd because you press them at once, but also depress them at once, so the caret 'readings' should be the same.

kasimir
  • 1,506
  • 1
  • 20
  • 26
0

go to the JSfiddle and fiddle with this:

a) depress 'q', then depress 'w', then release 'q', then release 'w' (fast enough to avoid autorepeat). That kind of sequence happens quite often when you type 'quickly' with more than one finger :).

b) leave any key depressed long enough for the autorepeat to kick in.

The differences are in plain view

Basically, keyUp fires when the physical key is released, but

  • another key can be depressed before the previous one is released
  • keyDown is called in case of autorepeat, while keyUp isn't

Since carret position and whatever text area updates are kept in sync with keyDown, you will simply miss the intermediate steps when monitoring keyUp.

In case (a), the keyUp handler sees carret pos jump from 0 to 2 as 'q' is released because two characters have been typed before the first key was released. The release of 'w' does not change carret position, so the handler ends up storing both q and w at position 2.

In case (b), the handler misses the repeats completely, and stores only the last instance of the key.

As a conclusion, using KeyUp will never achieve the intended meaning.

keyDown could, but personnally I would rather hook the changed event.

I reckon it is no less reliable to detect a bot filling the field (after all, a legit user could want to paste the password too), and you won't have to bother with control keys like backspace or whatever other non-keyboard means of clearing the input.

You could use keyUp as an extra paranoid check, as long as you don't expect to reconstruct the exact input. For instance, you could simply check that the released key matches the character at current cursor position. But frankly I'm not sure it is worth the bother.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43