O evento onKeyDown não está funcionando em divs no React

91

Quero usar um evento keyDown em um div no React. Eu faço:

  componentWillMount() {
      document.addEventListener("keydown", this.onKeyPressed.bind(this));
  }

  componentWillUnmount() {
      document.removeEventListener("keydown", this.onKeyPressed.bind(this));
  }      

  onKeyPressed(e) {
    console.log(e.keyCode);
  }

  render() {
    let player = this.props.boards.dungeons[this.props.boards.currentBoard].player;
    return (
      <div 
        className="player"
        style={{ position: "absolute" }}
        onKeyDown={this.onKeyPressed} // not working
      >
        <div className="light-circle">
          <div className="image-wrapper">
            <img src={IMG_URL+player.img} />
          </div>
        </div>
      </div>
    )
  }

Funciona bem, mas gostaria de fazer mais no estilo React. eu tentei

        onKeyDown={this.onKeyPressed}

no componente. Mas não reage. Ele funciona em elementos de entrada, se bem me lembro.

Codepen

Como eu posso fazer isso?

Michael
fonte
Pessoalmente, gosto da sua abordagem. Essa parece ser uma boa maneira de vincular os pressionamentos de tecla ao documento, o que está fora do escopo do seu componente. e não requer foco em um elemento específico.
Daniel

Respostas:

155

Você deve usar o atributo tabIndex para poder ouvir o onKeyDownevento em um div no React. A configuração tabIndex="0"deve disparar seu manipulador.

burakhan alcan
fonte
1
Parece um design ruim e teimoso. E se eu quiser manipular um evento bubbling em um elemento de contêiner, mas esse contêiner em si nunca deve ser focalizado?
Will P.
1
Isso parece mais um hack ou uma solução alternativa para mim. Pode haver um momento em que você precise que o div fornecido mantenha uma ordem de tabulação.
Iwnnay
8
tabIndex={-1}também pode ser usado se você estiver trabalhando com um elemento que não é interativo e não deseja alterar a ordem das guias do documento.
IliasT
1
você tem que definir o tabIndex, para tornar o div focalizável. Você pode definir o tabIndex como 0,1, -1 .... Mas antes de fazer isso verifique webaim.org/techniques/keyboard/tabindex Você provavelmente vai querer definir como -1, já que o está manipulando programaticamente .
Kavita
por algum motivo, isso só funciona se eu tiver um elemento <input> no div com o tabIndex. Ou qualquer outra coisa que possa ser focalizada. Com apenas outros divs, ainda não está capturando. Alguma ideia?
Flion
23

Você precisa escrever assim

<div 
    className="player"
    style={{ position: "absolute" }}
    onKeyDown={this.onKeyPressed}
    tabIndex="0"
  >

Se onKeyPressednão estiver vinculado a this, tente reescrevê-lo usando a função de seta ou vinculá-lo ao componente constructor.

Pantera
fonte
1
Desculpe. Eu tinha certeza, apenas esqueci isso. Mas não é esse o problema. Ainda não está funcionando.
Michael de
resposta atualizada. Você deve clicar no div ou colocá-lo em foco antes de pressionar qualquer tecla.
Panther de
Eu fiz. Não está funcionando. Eu atualizei o código acima. Funciona bem com os comandos DOM, mas não no estilo React.
Michael de
você pode explicar o que não está funcionando? sua função não está sendo chamada ou não está gerando console.lognada?
Panther de
A função não é chamada. Eu tenho um codepen: codepen.io/lafisrap/pen/OmyBYG . É sobre as linhas 275 e 307.
Michael,
6

Você está pensando muito em Javascript puro. Livre-se de seus ouvintes nesses métodos de ciclo de vida React e use em event.keyvez de event.keyCode(porque este não é um objeto de evento JS, é um React SyntheticEvent ). Todo o seu componente pode ser tão simples quanto isso (assumindo que você não vinculou seus métodos em um construtor).

onKeyPressed(e) {
  console.log(e.key);
}

render() {
  let player = this.props.boards.dungeons[this.props.boards.currentBoard].player;
  return (
    <div 
      className="player"
      style={{ position: "absolute" }}
      onKeyDown={(e) => this.onKeyPressed(e)}
    >
      <div className="light-circle">
        <div className="image-wrapper">
          <img src={IMG_URL+player.img} />
        </div>
      </div>
    </div>
  )
}
aço
fonte
É exatamente assim que eu queria escrever. Mas, diabos, não está indo. Aqui está o codepen: codepen.io/lafisrap/pen/OmyBYG . Se você comentar a linha 275, a entrada do teclado (teclas do cursor) não está funcionando. onKeyDown está na linha 307.
Michael,
keyCode Eu uso para as teclas do cursor, pois elas não retornam nenhum caractere.
Michael,
1
O React não usa objetos de evento Javascript, portanto, não há propriedade keyCode. Esse eventobjeto é um React SyntheticEvent .
aço de
@Michael Eu também não tenho certeza de como você está disparando um evento chave em um div, mas quando coloco esse código em um violino com um onClickprop em vez de onKeyDowndisparos do manipulador.
aço
Obrigado. Isso explica ... No entanto, os eventos principais funcionam em campos de entrada.
Michael,
1

A resposta com

<div 
    className="player"
    onKeyDown={this.onKeyPressed}
    tabIndex={0}
>

funciona para mim, observe que tabIndex requer um número, não uma string, portanto tabIndex = "0" não funciona.

Abelha atarefada
fonte
0

Usando o div truque com tab_index="0"ou tabIndex="-1"funciona, mas sempre que o usuário está focalizando uma visualização que não é um elemento, você obtém um esboço de foco feio em todo o site. Isso pode ser corrigido definindo o CSS para o div usar outline: noneno focus.

Esta é a implementação com componentes estilizados:

import styled from "styled-components"

const KeyReceiver = styled.div`
  &:focus {
    outline: none;
  }
`

e na classe App:

  render() {
    return (      
      <KeyReceiver onKeyDown={this.handleKeyPress} tabIndex={-1}>
          Display stuff...
      </KeyReceiver>
    )
Muppet
fonte
-1

Você está perdendo a vinculação do método no construtor. É assim que o React sugere que você faça:

class Whatever {
  constructor() {
    super();
    this.onKeyPressed = this.onKeyPressed.bind(this);
  }

  onKeyPressed(e) {
    // your code ...
  }

  render() {
    return (<div onKeyDown={this.onKeyPressed} />);
  }
}

Existem outras maneiras de fazer isso, mas será a mais eficiente em tempo de execução.

Iwnnay
fonte
-2

Você tem que evitar que o evento padrão seja acionado.

onKeyPressed(e) {
   e.preventDefault();
   console.log(e.key);
}
Boluwatife Fakorede
fonte