El evento onKeyDown no funciona en divs en React

92

Quiero usar un evento keyDown en un div en React. Hago:

  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 bien, pero me gustaría hacerlo más al estilo React. Lo intenté

        onKeyDown={this.onKeyPressed}

en el componente. Pero no reacciona. Funciona en elementos de entrada, según recuerdo.

Codepen

¿Cómo puedo hacerlo?

Miguel
fuente
Personalmente me gusta tu enfoque. Esta parece una buena forma de vincular las pulsaciones de teclas al documento, que está fuera del alcance de su componente. y no requiere centrarse en un elemento en particular.
Daniel

Respuestas:

159

Debe usar el atributo tabIndex para poder escuchar el onKeyDownevento en un div en React. La configuración tabIndex="0"debería despedir a su manejador.

burakhan alkan
fuente
2
Esto parece un diseño malo y obstinado. ¿Qué sucede si quiero manejar un evento de burbujeo en un elemento contenedor pero ese contenedor en sí no debería estar enfocado?
Will P.
1
Esto me parece más un truco o una solución alternativa. Puede haber un momento en el que necesite que el div dado mantenga un orden de tabulación.
Iwnnay
9
tabIndex={-1}también se puede usar si está trabajando con un elemento que no es interactivo y no desea cambiar el orden de tabulación del documento.
IliasT
1
tienes que configurar tabIndex, para que el div sea enfocable. Puede establecer tabIndex en 0,1, -1 .... Pero antes de hacerlo, consulte webaim.org/techniques/keyboard/tabindex Probablemente desee establecerlo en -1 ya que lo está manipulando programáticamente .
Kavita
por alguna razón, esto solo funciona si tengo un elemento <input> en el div con tabIndex. O algo más que pueda enfocarse. Con solo otros divs, todavía no se captura. ¿Alguna idea?
Flion
24

Necesitas escribirlo de esta manera

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

Si onKeyPressedno está vinculado a this, intente reescribirlo usando la función de flecha o vincularlo en el componente constructor.

Pantera
fuente
2
Lo siento. Estaba seguro, simplemente pasé por alto esto. Pero ese no es el problema. Todavía no funciona.
Michael
respuesta actualizada. Debe hacer clic en el div o enfocarlo antes de presionar cualquier tecla.
Panther
Yo hice. No funciona. Actualicé el código de arriba. Funciona bien con los comandos DOM, pero no en el estilo React.
Michael
¿Puedes explicar qué no funciona? ¿Su función no se llama o console.logno genera nada?
Panther
No se llama a la función. Tengo un codepen: codepen.io/lafisrap/pen/OmyBYG . Se trata de las líneas 275 y 307.
Michael
7

Estás pensando demasiado en Javascript puro. Deshágase de sus oyentes en esos métodos de ciclo de vida de React y utilícelos en event.keylugar de event.keyCode(porque este no es un objeto de evento JS, es un React SyntheticEvent ). Todo su componente podría ser tan simple como esto (suponiendo que no haya vinculado sus métodos en un constructor).

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={this.onKeyPressed}
    >
      <div className="light-circle">
        <div className="image-wrapper">
          <img src={IMG_URL+player.img} />
        </div>
      </div>
    </div>
  )
}
acero
fuente
Así es exactamente como quería escribirlo. Pero diablos, no va a funcionar. Aquí está el codepen: codepen.io/lafisrap/pen/OmyBYG . Si comenta la línea 275, la entrada del teclado (teclas del cursor) no funciona. onKeyDown está en la línea 307.
Michael
keyCode que utilizo para las teclas de cursor, ya que no devuelven ningún carácter.
Michael
1
React no usa objetos de evento de Javascript, por lo que no existe la propiedad keyCode. Ese eventobjeto es un React SyntheticEvent .
acero
@Michael Tampoco estoy del todo seguro de cómo está activando un evento clave en un div, pero cuando pongo este código en un violín con un onClickaccesorio en lugar del onKeyDowncontrolador se dispara.
acero
Gracias. Eso lo explica ... Sin embargo, los eventos clave funcionan en los campos de entrada.
Michael
2

La respuesta con

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

funciona para mí, tenga en cuenta que tabIndex requiere un número, no una cadena, por lo que tabIndex = "0" no funciona.

abeja ocupada
fuente
¿Quizás editar la otra respuesta?
Ross Attrill
0

Usar el divtruco con tab_index="0"o tabIndex="-1"funciona, pero cada vez que el usuario enfoca una vista que no es un elemento, obtienes un esquema de enfoque feo en todo el sitio web. Esto se puede solucionar configurando el CSS para que el div lo use outline: noneen el foco.

Aquí está la implementación con componentes con estilo:

import styled from "styled-components"

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

y en la clase App:

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

Falta el enlace del método en el constructor. Así es como React sugiere que lo hagas:

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

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

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

Hay otras formas de hacer esto, pero esta será la más eficiente en tiempo de ejecución.

Iwnnay
fuente
-3

Debe evitar que se active el evento predeterminado.

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