在JavaScript中是否可以覆盖keydown重复延迟?

目标是手动设置保持键的“重复率”。

例如,当在文本框中并按住X键时,我理解有特定于浏览器的方式来重复按下的字符 。 在某些情况下,它会暂停,然后不断触发按下的键。 在其他情况下,它根本不重复。 我想通过强制按下的键以特定间隔重复来缓解这种情况,无论浏览器如何。

通过研究,我想出了一个基于计时器的尝试,但在Safari中,它不会重复这个角色。 我有一个菜单系统,按住箭头滚动列表,但翻译动画和重复率不相互喜欢。

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 } ); 

我可能已经搞砸了整个事情……我试图重新创建这个SOpost的简化版本。 但是,我觉得必须有更好的方法来做到这一点。 有什么想法吗?

更新:

我们可以假设最终用户没有将其OS键盘重复率设置为大于我想要使用的速率(1000ms)。 如果是,那么它应该回落到它们的重复率,因为它不会继续触发按键事件。 如果不是(更可能是因为大多数人不修改它),那么我们将覆盖该行为以使其延迟我们指定的时间段。

好吧,我弄清楚为什么我的例子没有循环。 在keydown循环中,它在超时之前清除了超时:

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

应该在超时后清除超时,因此if条件需要移入超时function:

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

如果有人可以改进这一点,或者提供更好的选择,我将保留这笔赏金。

查看以下JavaScript文件 。 如果向下滚动到第530行,您将找到以下类:

 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."); }); }); 

作者所做的是他创建了一个特殊的Keyboard类来委派关键事件: keyupkeydownkeypress 。 该类只有一个构造函数,它接受一个参数 – keypress事件的间隔(这是你想要的)。 您可以使用Keyboard类实例的addEventListener方法添加事件侦听器:

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

请注意,上面的类依赖于以下框架: Lambda JS 。 您可以在此处查看上述脚本的工作演示。 希望这可以帮助。

更新1:

您的代码在Opera中不起作用。 此外,第二个事件在Firefox中额外延迟500毫秒后触发,并且连续事件不会保持相同的间隔。 此外,它无法同时处理多个关键事件。 让我们纠正这个问题:

首先,我们需要为Delta Timing创建一个简单的脚本,以便在持续间隔后触发关键事件。 我们使用以下代码段来创建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); } } 

接下来,我们编写逻辑来触发自定义keypressed事件。 我们需要自定义事件,因为我们必须能够同时处理多个键:

 (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); 

interval设置为1000毫秒,但您可以更改它。 最后注册我们做的事件:

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

这是简单而有效的JavaScript。 不需要jQuery。 你可以在这里看到现场演示 ,看看你和我的剧本之间的区别。 干杯。

更新2:

查看StackOverflow上的另一个问题 ,这是使用上述模式实现它的方法:

 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); 

使用上面的代码将消除您遇到的短暂延迟,并允许同时为不同的键触发多个事件。

你怎么做自定义键事件。 你可以听原始的(keyup / keydown),如果他们通过时间条件,你就会触发自定义事件。
这种方式的好处是您不依赖于计时器,它可以为您提供更多function,因为您使用自定义事件(顺便说一下,如果您愿意,可以跳过取消事件部分)。
这是一个演示,看看我在说什么: http : //jsfiddle.net/gion_13/gxEMz/
基本代码看起来像这样:

 $(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'); }); });