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

新手上路,请多包涵

目标是手动设置按住键的“重复率”。

例如,当在文本框中按住 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
} );

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

更新:

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

原文由 Jeff Jenkins 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 466
2 个回答

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

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

超时只有在超时后才应该被清除,所以需要将 if 条件移到超时函数中:

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

如果有人可以对此进行改进或提供更好的选择,我将保留此赏金。

原文由 Jeff Jenkins 发布,翻译遵循 CC BY-SA 3.0 许可协议

查看以下 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.");
    });
});

What the author has done is that he has created a special Keyboard class for delegating the key events: keyup , keydown and keypress .该类只有一个接受单个参数的构造函数 - keypress 事件的间隔(这就是你想要的)。您可以使用 --- 类实例的方法添加事件侦听器 addEventListener 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
});

请注意,上述类依赖于以下框架: 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 ms 但您可以更改它。最后注册我们做的事件:

 window.addEventListener("keypressed", function (event) {
    document.body.innerHTML += event.keyCode + " (" + event.time + " ms)<br/>";
}, 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);

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

原文由 Aadit M Shah 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题