Можно ли переопределить задержку повтора keydown в 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
} );

возможно, я все испортил...Я попытался воссоздать упрощенную версию это так пост. Тем не менее, я чувствую там и чтобы быть лучшим способом сделать это. Любой мысли?

обновление:

мы можем предположить, что конечный пользователь не установил скорость повтора клавиатуры ОС больше, чем скорость, которую я хочу использовать (1000 мс). Если это так, то он должен вернуться к их частоте повторения, так как он не будет продолжать запускать событие нажатия клавиши. Если это не так (более вероятно, поскольку большинство людей не изменяют это), то мы будем переопределять это поведение, чтобы заставить его задержать наш указанный период.

3 ответов


Ну, я понял, почему мой пример не зацикливание. В цикле 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 );
}

Я оставлю эту награду открытой на случай, если кто-то может улучшить это или предоставить лучшую альтернативу.


посмотрите на следующее файл 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 класс для делегирования ключевых событий: keyup, keydown и 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
});

обратите внимание, что вышеуказанный класс зависит от следующей структуры:лямбда JS. Вы можете увидеть рабочую демонстрацию вышеуказанного скрипта здесь. Надеюсь, это поможет.

обновление 1:

код код не работает в Opera. Кроме того, второе событие срабатывает после дополнительной задержки 500 мс в Firefox, а последовательные события не поддерживают тот же интервал. Плюс не может обрабатывать несколько ключевых событий одновременно. Давайте исправим эту проблему:

Сначала нам нужно создать простой скрипт для Дельта Времени так что ключевые события срабатывают после постоянного интервала. Мы используем следующий фрагмент для создания 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);
    }
}

Далее мы пишем логику для запуска custom 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 is установить в 1000 ms, но вы можете изменить это. Наконец, чтобы зарегистрировать событие мы делаем:

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

это простой и эффективный JavaScript. Нет в jQuery требуется. Вы можете увидеть live demo вот, и посмотрите разницу между вашим сценарием и моим. Овации.

обновление 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), и если они проходят условие времени, вы запускаете свое пользовательское событие.
Этот способ имеет преимущество, что вы не полагаетесь на таймеры, и это дает вам больше власти, потому что вы используете пользовательские события (кстати, вы можете пропустить часть события отмены, если хотите).
Вот демо, чтобы увидеть, о чем я говорю: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');
        });
});