In the wonderful Chromium browser (I think, this also applies to Google Chrome) keyboard events that can be handled by JavaScript, for some reason, behave in a very strange way, as for me. I came across two interesting manifestations of it:
1. The onkeyup event is generated immediately after the onkeydown event, and when the key is “clamped”, this pair of events starts to be generated with an enviable frequency of 25 milliseconds, but with a pause at the beginning.
2. If, after pressing and holding one key, pressing the next one - a pause of ~ 500 ms occurs, and then the onkeydown / onkeyup pair is already generated for the new key.
Below I will give my way to bypass such "cute" implementation bugs.
CAUTION! The method is not perfect, and for the most part it is a dirty hack than a working solution.
')
So let's get started.
Step 1. We know that the onkeydown / onkeyup bundle is generated approximately every 25 ms (derived experimentally). The first thing to do is assign handlers to these events. To do this, I create an object with handlers beforehand and assign them to document events on the onDomReady event.
For onDomReady, I used a method from jQuery, but it can successfully be replaced by any other.
KeyHandler = { <br/>
// <br/>
keyDown : function ( e ) { <br/>
} , <br/>
<br/>
// "" <br/>
keyUp : function ( e ) { <br/>
} , <br/>
<br/>
// , onDomReady . <br/>
init : function ( ) { <br/>
document. onkeydown = KeyHandler. keyDown ; <br/>
document. onkeyup = KeyHandler. keyUp ; <br/>
} <br/>
} <br/>
<br/>
$ ( document ) . ready ( function ( ) { <br/>
KeyHandler. init ( ) ; <br/>
} )
Step 2. In order to avoid “losing” events when pressing several keys in a row, it would be nice to organize some kind of structure for storing at least identifiers of what happened when a key was pressed.
We will also perform all keyboard events from the observer method, the start of which will be triggered during the onkeydown event, and which will terminate itself if there is no event in the event queue.
Let's change our event handlers to do useful work and add an "observer":
KeyHandler = { <br/>
<br/>
// KeyHandler , , <br/>
// "" . <br/>
pressed : { } , <br/>
// , , , <br/>
observed : false , <br/>
<br/>
// , , <br/>
observe : function ( ) { <br/>
<br/>
// , , . <br/>
var count = 0 ; <br/>
<br/>
// ,, , "". <br/>
// .. , . <br/>
for ( a in KeyHandler. pressed ) { <br/>
// <br/>
// - <br/>
// <br/>
count ++; <br/>
} <br/>
<br/>
// onkeyup - 25 . <br/>
// , , . <br/>
if ( count > 0 ) { <br/>
setTimeout ( KeyHandler. observe , 25 ) ; <br/>
} else { <br/>
KeyHandler. observed = false ; <br/>
} <br/>
} , <br/>
<br/>
// , onkeydown <br/>
startObserve : function ( ) { <br/>
// , observe <br/>
if ( KeyHandler. observed == false ) { <br/>
// , <br/>
KeyHandler. observed = true ; <br/>
// , <br/>
// ( ) 25 <br/>
setTimeout ( KeyHandler. observe , 25 ) ; <br/>
} <br/>
} , <br/>
<br/>
keyDown : function ( e ) { <br/>
// "". <br/>
KeyHandler. pressed [ e. keyCode ] = true ; <br/>
// . <br/>
KeyHandler. startObserve ( ) ; <br/>
} , <br/>
<br/>
keyUp : function ( e ) { <br/>
// "" <br/>
delete KeyHandler. pressed [ e. keyCode ] ; <br/>
} , <br/>
}
In general, the whole algorithm for bypassing bugs is quite simple - we add all the events to the queue, and if there is a keystroke event at the input, we try to start the processor for this event every 25 milliseconds.
But since we also remove this event from the queue every 25 milliseconds - the handler will not be able to go to infinity. At the same time, we get one invaluable plus - the event for the key already pressed when the second key is pressed will be saved (since onkeyup will not be executed if you press the second key), i.e. in our case, two parallel events will be executed for the key already pressed, for which the browser does not generate a pair of onkeydown / onkeyup events and for the second key pressed.
In the same way, we get rid of the pause in the events between the moment the second key is pressed and, in fact, the start of generating our notorious onkeydown / onkeyup pairs every ~ 25 milliseconds.
Step 3 . Now we will add something for which we have tried so hard - directly the methods that should be called when pressing (and holding) the keys.
KeyHandler = { <br/>
<br/>
// , <br/>
keyBinds : { <br/>
// <br/>
// - Chromium'a <br/>
40 : function ( ) { // DOWN <br/>
console. log ( "DOWN key fired" ) ; <br/>
} , <br/>
38 : function ( ) { // UP <br/>
console. log ( "UP key fired" ) ; <br/>
} , <br/>
39 : function ( ) { // RIGHT <br/>
console. log ( "RIGHT key fired" ) ; <br/>
} , <br/>
37 : function ( ) { // LEFT <br/>
console. log ( "LEFT key fired" ) ; <br/>
} <br/>
} , <br/>
<br/>
observe : function ( ) { <br/>
<br/>
var count = 0 ; <br/>
for ( a in KeyHandler. pressed ) { <br/>
<br/>
// <br/>
// <br/>
// <br/>
// <br/>
if ( typeof KeyHandler. keyBinds [ a ] == "function" ) { <br/>
setTimeout ( KeyHandler. keyBinds [ a ] , 0 ) ; <br/>
} <br/>
count ++; <br/>
} <br/>
<br/>
if ( count > 0 ) { <br/>
setTimeout ( KeyHandler. observe , 25 ) ; <br/>
} else { <br/>
KeyHandler. observed = false ; <br/>
} <br/>
} , <br/>
}
Well, a few final touches. Our handler can hang forever, if suddenly the window lost focus while holding the key. You can achieve this by clicking on another tab of the browser, without pressing a key. To avoid such a turn of events, we will additionally handle the onblur event and clean out the processing queue, if one happens.
KeyHandler = { <br/>
// <br/>
blur : function ( ) { <br/>
for ( a in KeyHandler. pressed ) { <br/>
delete KeyHandler. pressed [ a ] ; <br/>
} <br/>
} , <br/>
<br/>
init : function ( ) { <br/>
window. onkeydown = KeyHandler. keyDown ; <br/>
window. onkeyup = KeyHandler. keyUp ; <br/>
window. onblur = KeyHandler. blur ; <br/>
} , <br/>
}
That's all. Now keystrokes will be processed smoothly, and, most importantly, all together. As a development, you can add a definition to pressing key combinations.
I hope my example was useful to you.
PS The full code of the example can be found here:
http://elhsmart.net.ru/chrome-keyboard.htmlPPS Code was tested only in Chromium. My current version is 7.0.505.0. If something is wrong - write, try to fix it.