È ansible superare il ritardo di ripetizione Keydown, in JavaScript?

L'objective è impostare manualmente la "frequenza di ripetizione" di una chiave mantenuta.

Ad esempio, quando in una casella di text e tenendo premuto il tasto X, capisco che ci sono modi specifici per il browser di ripetere il carattere premuto . In alcuni, si interrompe, quindi in modo continuo innesca il tasto premuto. In altri, non si ripete affatto. Voglio mitigare questo forzando il tasto premuto per ripetere ad un intervallo specifico, indipendentemente dal browser.

Attraverso la ricerca, ho trovato un tentativo basato su timer, ma in Safari non ripete il carattere. Ho un sistema di menu in cui tiene premuto freccia giù attraverso l'elenco, ma l'animation di traduzione e la frequenza di ripetizione non si piacciono.

var repeating = false; var repeatRateTimer = null; $( document ).bind( 'keyup', function( input ) { if( repeatRateTimer != null ) { clearTimeout( repeatRateTimer ); repeatRateTimer = null; } repeating = false; } ); $( document ).bind( 'keydown', function( input ) { input.preventDefault( ); if( repeating == true ) { if( repeatRateTimer != null ) { clearTimeout( repeatRateTimer ); repeatRateTimer = null; } else { repeatRateTimer = setTimeout( function( ){ repeating = false; }, 1000 ); } return; } repeating = true; // ...keyboard logic } ); 

Potrei aver sminuito tutto questo … Ho cercato di ricreare una versione semplificata di questo post SO . Tuttavia, credo che ci sia un modo migliore per farlo. qualche idea?

Aggiornare:

Possiamo supporre che l'utente finale non abbia impostato la frequenza di ripetizione della tastiera OS superiore alla velocità che voglio utilizzare (1000 ms). Se è così, dovrebbe tornare alla loro frequenza di ripetizione, in quanto non continuerà a far scattare l'evento di printing chiave. Se non lo è (più probabile perché la maggior parte delle persone non lo modifica), allora saremmo costantemente in grado di far ritardare il nostro periodo specificato.

Beh, ho capito perché il mio esempio non era looping. Nel ciclo keydown, stava cancellando il timeout prima che fosse scaduto:

 if( repeatRateTimer != null ) { clearTimeout( repeatRateTimer ); repeatRateTimer = null; } else { repeatRateTimer = setTimeout( function( ){ repeating = false; }, 1000 ); } 

Il timeout deve essere eliminato solo dopo la scadenza, quindi la condizione necessaria per essere spostata nella function di timeout:

 if( repeatRateTimer == null ) { repeatRateTimer = setTimeout( function( ) { repeating = false; clearTimeout( repeatRateTimer ); repeatRateTimer = null; }, 1000 ); } 

Lascerò questa bontà aperta nel caso in cui qualcuno possa migliorare su questo, o fornire una migliore alternativa.

Guarda il seguente file JavaScript . Se scorri fino alla linea 530 troverai la seguente class:

 var Keyboard = new Class(function (constructor) { var key = {}; var eventListener = { keyup: {}, keydown: {}, keypress: {} }; constructor.overload(["Number"], function (interval) { setInterval(keypress, interval); }); window.addEventListener("keyup", keyup, false); window.addEventListener("keydown", keydown, false); function keyup(event) { var keyCode = event.keyCode; var listener = eventListener.keyup[keyCode]; key[keyCode] = false; if (listener) listener(); } function keydown(event) { var keyCode = event.keyCode; var listener = eventListener.keydown[keyCode]; key[keyCode] = true; if (listener) listener(); } function keypress() { for (var code in key) { var listener = eventListener.keypress[code]; if (key[code] && listener) listener(); } } this.addEventListener = new Dispatcher(["String", "Number", "Function"], function (type, keyCode, listener) { type = eventListener[type]; if (type) type[keyCode] = listener; else throw new Error("Unexpected value for type."); }); }); 

Quello che l'autore ha fatto è che ha creato una speciale class di Keyboard per debind gli events chiave: keyup , keydown e keypress . La class ha solo un constructor che accetta un singolo argomento – l'intervallo dell'evento keypress (che è quello che vuoi). È ansible aggiungere ascoltatori di events utilizzando il metodo addEventListener dell'istanza della class Keyboard :

 var keyboard = new Keyboard(125); // fire key press 8 times a second. keypress.addEventListener("keypress", 65, function () { // do something every time A is pressed }); 

Si noti che la class precedente dipende dal seguente schema: Lambda JS . Qui potete vedere una demo di lavoro dello script di cui sopra. Spero che questo ti aiuti.

Aggiornamento 1:

Il tuo codice non funziona in Opera. Inoltre, il secondo evento si spegne dopo un ritardo di 500 ms in Firefox e gli events consecutivi non mantengono lo stesso intervallo. Inoltre, non è in grado di gestire più events chiave contemporaneamente. Correggiamo questo problema:

Innanzitutto dobbiamo creare un semplice script per Delta Timing in modo che gli events chiave sparano dopo un intervallo costante. Utilizziamo il seguente frammento per la creazione di un DeltaTimer :

 function DeltaTimer(render, interval) { var timeout; var lastTime; this.start = start; this.stop = stop; function start() { timeout = setTimeout(loop, 0); lastTime = Date.now(); return lastTime; } function stop() { clearTimeout(timeout); return lastTime; } function loop() { var thisTime = Date.now(); var deltaTime = thisTime - lastTime; var delay = Math.max(interval - deltaTime, 0); timeout = setTimeout(loop, delay); lastTime = thisTime + delay; render(thisTime); } } 

Successivamente scriviamo la logica per sparare eventuali events keypressed personalizzati. Abbiamo bisogno di events personalizzati poiché dobbiamo essere in grado di gestire più chiavi contemporaneamente:

 (function (interval) { var keyboard = {}; window.addEventListener("keyup", keyup, false); window.addEventListener("keydown", keydown, false); function keyup(event) { keyboard[event.keyCode].pressed = false; } function keydown(event) { var keyCode = event.keyCode; var key = keyboard[keyCode]; if (key) { if (!key.start) key.start = key.timer.start(); key.pressed = true; } else { var timer = new DeltaTimer(function (time) { if (key.pressed) { var event = document.createEvent("Event"); event.initEvent("keypressed", true, true); event.time = time - key.start; event.keyCode = keyCode; window.dispatchEvent(event); } else { key.start = 0; timer.stop(); } }, interval); key = keyboard[keyCode] = { pressed: true, timer: timer }; key.start = timer.start(); } } })(1000); 

L' interval è impostato a 1000 ms, ma è ansible modificarlo. Infine per registrare un evento che facciamo:

 window.addEventListener("keypressed", function (event) { document.body.innerHTML += event.keyCode + " (" + event.time + " ms)<br/>"; }, false); 

Questo è JavaScript semplice ed efficiente. Nessun jQuery richiesto. Qui puoi vedere la demo live e vedere la differenza tra il tuo script e la mia. Saluti.

Aggiornamento 2:

Guardando l'altra domanda su StackOverflow, questo è come si avrebbe implementato utilizzando il model di cui sopra:

 window.addEventListener("keypressed", function (event) { switch (event.keyCode) { case 37: Move(-1, 0); break; case 38: Move(0, -1); break; case 39: Move(1, 0); break; case 40: Move(0, 1); break; } }, false); 

L'utilizzo del codice riportto sopra rimuoverà il breve ritardo che si sta verificando e consente inoltre l'triggerszione di più events per diverse chiavi contemporaneamente.

Che ne dici di fare events chiave personalizzati. è ansible ascoltare quelli originali (keyup / keydown) e se passano la condizione di tempo, si innesca l'evento personalizzato.
In questo modo si ha il vantaggio di non fare affidamento sui timer e ti dà più potenza, perché si utilizza events personalizzati (btw, si può saltare la parte dell'evento di annullamento se lo si desidera).
Ecco una dimostrazione per vedere cosa sto parlando: http://jsfiddle.net/gion_13/gxEMz/
E il codice base sembra qualcosa di simile:

 $(document).ready(function(){ var dispatcher = $(window), keyRate = 1000, //ms lastKeyEvent = 0, cancelEvent = function(e){ var evt = e ? e:window.event; if(evt.stopPropagation) evt.stopPropagation(); if(evt.cancelBubble!=null) evt.cancelBubble = true; return false; }; dispatcher .bind('keydown',function(e){ var now = new Date().getTime(); if(now - lastKeyEvent <= keyRate) // cancel the event return cancelEvent(e); var keyEventsTimeDiff = now - lastKeyEvent; lastKeyEvent = now; dispatcher.trigger('special-keydown',[e,keyEventsTimeDiff ]); }) .bind('keyup',function(e){ cancelEvent(e); dispatcher.trigger('special-keyup',[e]); }) // binding the custom events .bind('special-keydown',function(e,keyEventsTimeDiff){ console.log(e,'special keydown triggered again after ' + keyEventsTimeDiff +'ms'); }) .bind('special-keyup',function(e,keyEventsTimeDiff){ console.log(e,'special keyup'); }); });