L'événement onKeyDown ne fonctionne pas sur les divs dans React


91

Je souhaite utiliser un événement keyDown sur un div dans React. Je fais:

  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>
    )
  }

Cela fonctionne bien, mais j'aimerais le faire plus dans le style React. j'ai essayé

        onKeyDown={this.onKeyPressed}

sur le composant. Mais ça ne réagit pas. Cela fonctionne sur les éléments d'entrée comme je me souviens.

Codepen

Comment puis-je le faire?


Personnellement, j'aime votre approche. Cela semble être un bon moyen de lier les touches au document, ce qui est en dehors de la portée de votre composant. et ne nécessite pas de se concentrer sur un élément particulier.
Daniel

Réponses:


156

Vous devez utiliser l' attribut tabIndex pour pouvoir écouter l' onKeyDownévénement sur un div dans React. Le réglage tabIndex="0"devrait mettre le feu à votre gestionnaire.


1
Cela semble être une conception mauvaise et opiniâtre. Que faire si je souhaite gérer un événement bouillonnant sur un élément de conteneur mais que ce conteneur lui-même ne doit jamais être ciblé?
Will P.

1
Cela me semble plus être un hack ou une solution de contournement. Il peut arriver que vous deviez demander à la div donnée de maintenir un ordre de tabulation.
Iwnnay

8
tabIndex={-1}peut également être utilisé si vous travaillez avec un élément qui n'est pas interactif et que vous ne souhaitez pas modifier l'ordre de tabulation du document.
IliasT

1
vous devez définir le tabIndex, pour rendre le div focusable. Vous pouvez définir le tabIndex sur 0,1, -1 .... Mais avant de faire cela, vérifiez ceci sur webaim.org/techniques/keyboard/tabindex Vous voudrez probablement mettre -1 car vous le manipulez par programme .
Kavita

pour une raison quelconque, cela ne fonctionne que si j'ai un élément <input> dans le div avec le tabIndex. Ou quelque chose d'autre qui peut être ciblé. Avec juste d'autres divs, ce n'est toujours pas capturant. Des pensées?
Flion

23

Vous devez l'écrire de cette façon

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

Si onKeyPressedn'est pas lié à this, essayez de le réécrire à l'aide de la fonction de flèche ou de le lier dans le composant constructor.


1
Désolé. J'étais sûr, j'ai juste oublié cela. Mais ce n'est pas le problème. Cela ne fonctionne toujours pas.
Michael

réponse mise à jour. Vous devez soit cliquer sur le div ou l'amener au focus avant d'appuyer sur une touche.
Panther

J'ai fait. Ça ne fonctionne pas. J'ai mis à jour le code ci-dessus. Cela fonctionne bien avec les commandes DOM, mais pas dans le style React.
Michael

pouvez-vous expliquer ce qui ne fonctionne pas? votre fonction n'est-elle pas appelée ou console.logne produit rien?
Panther

La fonction n'est pas appelée. J'ai un codepen: codepen.io/lafisrap/pen/OmyBYG . Il s'agit des lignes 275 et 307.
Michael

6

Vous pensez trop en Javascript pur. Débarrassez-vous de vos écouteurs sur ces méthodes de cycle de vie React et utilisez à la event.keyplace de event.keyCode(car il ne s'agit pas d'un objet événement JS, il s'agit d'un événement synthétique React ). Votre composant entier pourrait être aussi simple que cela (en supposant que vous n'ayez pas lié vos méthodes dans un constructeur).

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>
  )
}

C'est exactement comme ça que j'ai voulu l'écrire. Mais bon sang, ça ne va pas. Voici le codepen: codepen.io/lafisrap/pen/OmyBYG . Si vous commentez la ligne 275, la saisie au clavier (touches de curseur) ne fonctionne pas. onKeyDown est en ligne 307.
Michael

keyCode J'utilise pour les touches de curseur, car elles ne renvoient aucun caractère.
Michael

1
React n'utilise pas d'objets d'événement Javascript, il n'y a donc pas de propriété keyCode. Cet eventobjet est un React SyntheticEvent .
acier

@Michael Je ne suis pas non plus tout à fait sûr de savoir comment vous déclenchez un événement clé sur un div, mais quand je mets ce code dans un violon avec un onClickaccessoire au lieu de onKeyDowndéclencher le gestionnaire.
acier

Merci. Cela explique cela ... Les événements clés fonctionnent cependant dans les champs de saisie.
Michael

1

La réponse avec

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

fonctionne pour moi, veuillez noter que tabIndex nécessite un nombre, pas une chaîne, donc tabIndex = "0" ne fonctionne pas.


0

L'utilisation de l' divastuce avec tab_index="0"ou tabIndex="-1"fonctionne, mais chaque fois que l'utilisateur se concentre sur une vue qui n'est pas un élément, vous obtenez un aperçu de la mise au point laide sur l'ensemble du site Web. Cela peut être résolu en définissant le CSS pour le div à utiliser outline: nonedans le focus.

Voici l'implémentation avec des composants stylisés:

import styled from "styled-components"

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

et dans la classe App:

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

-1

Il vous manque la liaison de la méthode dans le constructeur. Voici comment React vous propose de le faire:

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

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

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

Il existe d'autres moyens de le faire, mais ce sera le plus efficace lors de l'exécution.


-2

Vous devez empêcher l'événement par défaut de se déclencher.

onKeyPressed(e) {
   e.preventDefault();
   console.log(e.key);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.