Verifique se uma chave está abaixada?

91

Existe uma maneira de detectar se uma chave está atualmente inativa no JavaScript?

Eu sei sobre o evento "keydown", mas não é disso que preciso. Algum tempo APÓS a tecla ser pressionada, quero ser capaz de detectar se ela ainda está pressionada.

PS O maior problema parece ser que depois de algum tempo a tecla começa a se repetir, disparando eventos keydown e keyup como um demônio. Esperançosamente, há apenas uma função isKeyDown (tecla) simples, mas se não for esse o problema, será necessário superar / contornar.

Daniel X Moore
fonte
6
Um problema comum com as respostas que vejo aqui é que se você mantiver uma tecla pressionada, alterar as guias ou mudar o foco, deixar a tecla subir e voltar, o código acreditará que a tecla está abaixada até que você pressione-a novamente ou mova o mouse sobre a página. :-(
Eric Mickelsen

Respostas:

71

Existe uma maneira de detectar se uma chave está atualmente inativa no JavaScript?

Não. A única possibilidade é monitorar cada um keyupe keydowne lembrar.

depois de algum tempo, a tecla começa a se repetir, disparando eventos keydown e keyup como um demônio.

Não deveria. Você com certeza se repetirá keypresse, em muitos navegadores, também se repetirá keydown, mas se keyuprepetir, é um bug.

Infelizmente, não é um bug completamente inédito: no Linux, Chromium e Firefox (quando está sendo executado em GTK +, que está em distros populares como o Ubuntu), ambos geram sequências repetidas de keyup-keypress-keydown para chaves retidas, que são impossíveis de distinguir de alguém martelando a chave muito rápido.

bobince
fonte
14
O senhor é um cavalheiro e um estudioso. Chromium e Firefox no Ubuntu são meu principal ambiente de desenvolvimento, então isso explica com precisão o problema que tenho visto. Esperançosamente, ficará melhor, caso contrário, essa solução de hack do temporizador pode ser a única solução alternativa.
Daniel X Moore
3
Sim, é frustrante que não haja progresso nisso. Veja bugs.launchpad.net/ubuntu/+bug/369880 . Estou escrevendo um jogo de navegador e minha solução no momento é me ater às teclas modificadoras (shift, ctrl, etc.) que não se repetem.
bobince
2
Não, é possível distingui-los de pressionamentos de tecla repetidos genuínos por sua falta de keyupeventos correspondentes .
mako de
4
ainda é o caso, 8 anos depois? Esta resposta pode precisar ser atualizada.
Marco Lavagnino
3
ajuda de análise: (Linux && (Chromium || (Firefox && GTK +)))
bobince
134

Além de usar keyupe keydownouvintes para rastrear quando uma tecla desce e sobe, na verdade existem algumas propriedades que informam se certas teclas estão pressionadas.

window.onmousemove = function (e) {
  if (!e) e = window.event;
  if (e.shiftKey) {/*shift is down*/}
  if (e.altKey) {/*alt is down*/}
  if (e.ctrlKey) {/*ctrl is down*/}
  if (e.metaKey) {/*cmd is down*/}
}

Este estão disponíveis em todos os objetos de eventos do navegador gerado, tais como os de keydown, keyupe keypress, para que você não tem que usar mousemove.

Tentei gerar meus próprios objetos de evento com document.createEvent('KeyboardEvent')e document.createEvent('KeyboardEvent')e procurando e.shiftKeye tal, mas não tive sorte.

Estou usando o Chrome 17 no Mac

Devin G Rhode
fonte
Estou usando isso, em navegadores novos e antigos, até mesmo HTAs
Jakob Sternberg
1
Existe uma maneira de implementar essa resposta quando algum número está baixo? Eu tentei com if (e.keyCode == 49) {console.log ("1 is down");} mas não funciona :(
Roberto Sepúlveda Bravo
Ei @RobertoSepúlvedaBravo Robert parece responder sua pergunta na próxima resposta. ;)
Mark Odey,
Na verdade, você não deveria estar procurando por e.shiftKey no keyup, se você quer dizer no Shift. Use e.shiftKey em, por exemplo, onclick.
maciek
45

Minha solução:

var pressedKeys = {};
window.onkeyup = function(e) { pressedKeys[e.keyCode] = false; }
window.onkeydown = function(e) { pressedKeys[e.keyCode] = true; }

Agora posso verificar se alguma tecla foi pressionada em qualquer outra parte do script verificando

pressedKeys["code of the key"]

Se for verdade, a tecla está pressionada.

Robert
fonte
2
Como keys já é uma função, um nome de variável diferente pode ser melhor.
Raven
1
isso não funcionará para detectar se a tecla Alt está pressionada ou não em todos os casos.
Michael
1
Isso é ótimo para detectar cima / baixo / esquerda / direita
Jonathan Spiller
11

Não acredito que exista uma função isKeyDown, mas você pode escrever a sua própria.

Basicamente, crie um array cujo comprimento seja o número de chaves que você deseja monitorar. Em seguida, usando os eventos documentos / páginas / controles keyUp e keyDown, atualize o array com o estado dessa chave.

Em seguida, escreva uma função que verifique se uma determinada tecla está pressionada e retorne um bool.

var keyEnum = { W_Key:0, A_Key:1, S_Key:2, D_Key:3 };
var keyArray = new Array(4);

function onKeyDown()
{
    // Detect which key was pressed
    if( key == 'w' )
        keyArray[keyEnum.W_Key] = true;
    // Repeat for each key you care about...
}

function onKeyUp()
{
    // Detect which key was released
    if( key == 'w' )
        keyArray[keyEnum.W_Key] = false;
    // Repeat for each key you care about...
}

function isKeyDown(key)
{
    return keyArray[key];
}

Isso deve realizar o que você deseja.

FallenAvatar
fonte
1
Isso é bom e, de fato, parte da solução, mas não resolve o bug de repetição de keyup que estou enfrentando. Veja a resposta de bobince.
Daniel X Moore
4
Esta não é uma boa solução, pois você estaria escrevendo mais e mais ifs. Uma "keyList = {};" ser um objeto aceita "keyList [key] = true;" sem a necessidade de um enum ou limite, pois usa índices / propriedades de string e funciona para todas as chaves.
SparK de
5

Acabei aqui para verificar se já existia algo embutido no navegador, mas parece que não existe. Esta é a minha solução (muito semelhante à resposta de Robert):

"use strict";

const is_key_down = (() => {
    const state = {};

    window.addEventListener('keyup', (e) => state[e.key] = false);
    window.addEventListener('keydown', (e) => state[e.key] = true);

    return (key) => state.hasOwnProperty(key) && state[key] || false;
})();

Você pode então verificar se uma tecla foi pressionada com is_key_down('ArrowLeft').

antonagestam
fonte
1

Outras pessoas já fizeram esse tipo de pergunta antes (embora eu não veja nenhum tolo óbvio aqui agora).

Acho que a resposta é que o keydownevento (e seu gêmeo keyup) são todas as informações que você obtém. A repetição está conectada com bastante firmeza ao sistema operacional, e um programa de aplicativo não tem muitas oportunidades de consultar o BIOS para o estado real da chave.

O que você pode fazer, e talvez tenha que fazer se precisar fazer isso funcionar, é desfazer o salto da chave programaticamente. Essencialmente, você pode avaliar keydowne keyupvocê mesmo, mas ignorar um keyupevento se ele ocorrer muito rapidamente após o último keydown... ou essencialmente, você deve atrasar sua resposta por keyuptempo suficiente para ter certeza de que não há outro keydownevento seguinte com algo como 0,25 segundo do keyup.

Isso envolveria usar uma atividade de cronômetro e registrar as vezes em milissegundos para eventos anteriores. Não posso dizer que seja uma solução muito atraente, mas ...

Carl Smotricz
fonte
2
Eu estava preocupado que pudesse chegar a esse ponto.
Daniel X Moore
1
/*
Tracks what keys are currently down on the keyboard
*/

function keyboard_module(onUpdate){
    var kb = {};
    var unicode_mapping = {};
    document.onkeydown = function(e){
        var unicode=e.charCode? e.charCode : e.keyCode
        var key = getKey(unicode);
        kb[key] = true;
        if(onUpdate){
            onUpdate(kb);
        }
    }

    document.onkeyup = function(e){
        var unicode=e.charCode? e.charCode : e.keyCode
        var key = getKey(unicode);
        delete kb[key];
        if(onUpdate){
            onUpdate(kb);
        }
    }

    function getKey(unicode){
        if(unicode_mapping[unicode]){
            var key = unicode_mapping[unicode];
        }else{
            var key= unicode_mapping[unicode] = String.fromCharCode(unicode);
        }
        return key;
    }
    return kb;
}

function testing(kb){
    console.log('These are the down keys', kb);
}


var keyboard = keyboard_module(testing);

....
//somewhere else in the code
if(keyboard['K']){/*do something special */}
RobKohr
fonte
Não tenho certeza se String.fromCharCode (unicode); é uma pesquisa rápida ou não, é por isso que tenho o objeto unicode_mapping. Isso pode ser algo a ser extraído para reduzir um pouco esse código se ele for ultrarrápido. Como ele será chamado repetidamente para pressionamentos de tecla, a velocidade é importante, por isso adicionei o mapeamento de forma pessimista.
RobKohr
1

O código a seguir é o que estou usando:

var altKeyDownCount = 0;
window.onkeydown = function (e) {
    if (!e) e = window.event;
    if (e.altKey) {
        altKeyDownCount++;
        if (30 < altKeyDownCount) {
            $('.key').removeClass('hidden');
            altKeyDownCount = 0;
        }
        return false;
    }
}

window.onkeyup = function (e) {
    if (!e) e = window.event;
    altKeyDownCount = 0;
    $('.key').addClass('hidden');
}

Quando o usuário mantém pressionada a tecla Alt por algum tempo (cerca de 2 segundos), um grupo de rótulos (classe = 'tecla oculta') aparece. Quando a tecla Alt é liberada, os rótulos desaparecem. jQuery e Bootstrap são usados.

Johnny
fonte
e o que acontece se "Tab" for pressionado enquanto Alt estiver pressionado?
Michael
1

Eu sei que esta é uma questão muito antiga, no entanto, há uma biblioteca JavaScript muito leve (~ .5Kb) que efetivamente "corrige" o disparo inconsistente de manipuladores de eventos de teclado ao usar a API DOM.

A biblioteca é Keydrown .

Aqui está o exemplo de código operativo que funcionou bem para meus objetivos, apenas alterando a chave na qual definir o ouvinte:

kd.P.down(function () {
  console.log('The "P" key is being held down!');
});

kd.P.up(function () {
  console.clear();
});

// This update loop is the heartbeat of Keydrown
kd.run(function () {
  kd.tick();
});

Eu incorporei o Keydrown em meu JavaScript do lado do cliente para uma animação de pausa adequada em um jogo Red Light Green Light que estou escrevendo. Você pode ver o jogo completo aqui . (Observação: se você estiver lendo isso no futuro, o jogo deve ter o código completo e ser jogável :-D!)

Eu espero que isso ajude.

buildpax
fonte
1

Eu examinei as respostas acima e a abordagem keydown/ proposta keyupfunciona apenas em circunstâncias especiais. Se o usuário Alt-tab se distanciar ou usar um gesto de tecla para abrir uma nova janela ou guia do navegador, então um keydownserá registrado, o que é bom, porque nesse ponto é impossível dizer se a tecla é algo que o aplicativo da web está monitorando ou é um navegador padrão ou atalho do sistema operacional. Voltando à página do navegador, ele ainda pensará que a chave está segura, embora tenha sido lançada nesse meio tempo. Ou alguma tecla é simplesmente mantida pressionada, enquanto o usuário muda para outra guia ou aplicativo com o mouse, e então solta fora de nossa página.

As teclas modificadoras ( Shiftetc.) podem ser monitoradas via mousemoveetc., assumindo que há pelo menos uma interação do mouse esperada ao voltar a tabulação, o que é frequentemente o caso.

Para a maioria todas as outras teclas (exceto modificadores, Tab, Delete, mas incluindo Space, Enter), monitoramento keypressiria trabalhar para a maioria das aplicações - uma tecla pressionada continuará a fogo. Porém, há alguma latência na redefinição da chave, devido à periodicidade do keypressdisparo. Basicamente, se keypressnão continuar disparando, é possível descartar a maioria das chaves. Isso, combinado com os modificadores, é bastante hermético, embora eu não tenha explorado o que fazer com Tabe Backspace.

Tenho certeza de que há alguma biblioteca por aí que resume essa fraqueza do DOM, ou talvez alguma mudança no padrão do DOM tenha cuidado disso, já que é uma questão bastante antiga.

Robert Monfera
fonte
keypress agora está obsoleto. Parece não funcionar no Chrome.
eadsjr
0

Olhe para esta resposta e use onkeyupe onkeydown. Aqui estão informações mais específicas sobre esses eventos.

Claudiu
fonte
1
O link que você cita é para os eventos do mouse, onde a repetição automática não é um problema.
Carl Smotricz
key-up e key-down não se repetem. pressionar a tecla pode.
Claudiu
bem, mesmo que o façam, eu estava imaginando algo como a solução do TJMonk15, só não queria escrever
Claudiu
1
O texto dessas duas perguntas é exatamente o mesmo. Estranha coincidência ou algo mais?
Mitch Lindgren
2
@Mitch: uau ... isso é incrível. não sei por que alguém faria duas contas para fazer a mesma pergunta. ou mais provavelmente essa pessoa apenas copiou a outra pergunta e a adaptou para as chaves
Claudiu
0

Isso funciona no Firefox e no Chrome.

Tive a necessidade de abrir localmente um arquivo html especial (pressionando Enterquando o arquivo é selecionado no explorador de arquivos do Windows), seja apenas para visualizar o arquivo ou para editá-lo em um editor online especial.

Portanto, eu queria distinguir entre essas duas opções mantendo pressionada a Ctrltecla -ou não, enquanto pressionava Enter.

Como todos vocês compreenderam a partir de todas as respostas aqui, isso parece não ser realmente possível, mas aqui está uma maneira que imita esse comportamento de uma forma que era aceitável para mim.

A maneira como isso funciona é assim:

Se você mantiver pressionada a Ctrltecla-ao abrir o arquivo, um evento keydown nunca será disparado no código javascript. Mas um evento keyup será acionado (quando você finalmente soltar a Ctrltecla -key). O código captura isso.

O código também desativa os eventos de tecla (keyup e keydown) assim que um deles ocorre. Portanto, se você pressionar a Ctrltecla-após a abertura do arquivo, nada acontecerá.

window.onkeyup = up;
window.onkeydown = down;
function up(e) {
  if (e.key === 'F5') return; // if you want this to work also on reload with F5.

  window.onkeyup = null;
  window.onkeyup = null;
  if (e.key === 'Control') {
    alert('Control key was released. You must have held it down while opening the file, so we will now load the file into the editor.');
  }         
}
function down() {
  window.onkeyup = null;
  window.onkeyup = null;
}
Magnus
fonte
-3
$('#mytextbox').keydown(function (e) {
            if (e.keyCode == 13) {
                if (e.altKey) {
                    alert("alt is pressed");
                }
            }
 });

se você pressionar alt + enter, verá o alerta.

Mokarom
fonte