Zdarzenie onKeyDown nie działa na elementach div w Reakcie

92

Chcę użyć zdarzenia keyDown na div w Reakcie. Ja robię:

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

Działa dobrze, ale chciałbym to zrobić bardziej w stylu React. próbowałem

        onKeyDown={this.onKeyPressed}

na komponencie. Ale to nie reaguje. O ile pamiętam, działa na elementach wejściowych.

Codepen

Jak mogę to zrobić?

Michał
źródło
Osobiście podoba mi się Twoje podejście. Wygląda to na dobry sposób na powiązanie naciśnięć klawiszy z dokumentem, który jest poza zakresem twojego komponentu. i nie wymaga skupienia się na konkretnym elemencie.
Daniel

Odpowiedzi:

159

Powinieneś użyć atrybutu tabIndex, aby móc nasłuchiwać onKeyDownzdarzenia elementu div w Reakcie. Ustawienie tabIndex="0"powinno zwolnić program obsługi.

burakhan alkan
źródło
2
Wydaje się, że to zły, uparty projekt. Co się stanie, jeśli chcę obsłużyć zdarzenie bulgotania w elemencie kontenera, ale sam kontener nie powinien być nigdy skoncentrowany?
Will P.
1
Wydaje mi się, że to raczej hack lub obejście. Może zaistnieć potrzeba, aby dany element div utrzymywał kolejność tabulacji.
Iwnnay
9
tabIndex={-1}może być również używany, jeśli pracujesz z elementem, który nie jest interaktywny i nie chcesz zmieniać kolejności tabulacji w dokumencie.
IliasT
1
musisz ustawić tabIndex, aby element div był aktywny. Możesz ustawić tabIndex na 0,1, -1 .... Ale zanim to zrobisz sprawdź to webaim.org/techniques/keyboard/tabindex Prawdopodobnie będziesz chciał ustawić na -1, ponieważ manipulujesz nim programowo .
Kavita
z jakiegoś powodu działa to tylko wtedy, gdy mam element <input> w div z tabIndex. Lub coś innego, na czym można się skupić. W przypadku tylko innych elementów div nadal nie jest przechwytywany. jakieś pomysły?
Flion
24

Musisz to napisać w ten sposób

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

Jeśli onKeyPressednie jest powiązany this, spróbuj przepisać go za pomocą funkcji strzałek lub powiązać w komponencie constructor.

Pantera
źródło
2
Przepraszam. Byłem pewien, właśnie to przeoczyłem. Ale to nie jest problem. Nadal nie działa.
Michael
zaktualizowana odpowiedź. Należy kliknąć element DIV lub ustawić na nim fokus przed naciśnięciem jakichkolwiek klawiszy.
Panther
Zrobiłem. To nie działa. Zaktualizowałem powyższy kod. Działa dobrze z poleceniami DOM, ale nie w stylu React.
Michael
czy możesz wyjaśnić, co nie działa? Twoja funkcja nie jest wywoływana lub console.lognic nie wyświetla?
Panther
Funkcja nie jest wywoływana. Mam kod: codepen.io/lafisrap/pen/OmyBYG . Chodzi o linie 275 i 307.
Michael
7

Za dużo myślisz w czystym JavaScript. Pozbądź się słuchaczy na tych metodach cyklu życia React i używaj event.keyzamiast nich event.keyCode(ponieważ nie jest to obiekt zdarzenia JS, jest to React SyntheticEvent ). Twój cały komponent może być tak prosty (zakładając, że nie ograniczyłeś swoich metod w konstruktorze).

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>
  )
}
stal
źródło
Dokładnie tak chciałem to napisać. Ale do diabła, to nie idzie. Oto codepen: codepen.io/lafisrap/pen/OmyBYG . Jeśli skomentujesz linię 275, wtedy wejście klawiatury (klawisze kursora) nie działa. onKeyDown jest w linii 307.
Michael
keyCode, którego używam dla klawiszy kursora, ponieważ nie zwracają żadnych znaków.
Michael
1
React nie używa obiektów zdarzeń JavaScript, więc nie ma właściwości keyCode. Ten eventobiekt to React SyntheticEvent .
stal
@Michael też nie jestem do końca pewien, w jaki sposób wyzwalasz kluczowe zdarzenie w elemencie div, ale kiedy umieszczam ten kod w skrzypcach z onClickrekwizytem, ​​zamiast programu onKeyDownobsługi odpala.
stal
Dzięki. To wyjaśnia ... Jednak kluczowe zdarzenia działają w polach wejściowych.
Michael
2

Odpowiedź z

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

działa dla mnie, pamiętaj, że tabIndex wymaga liczby, a nie ciągu, więc tabIndex = "0" nie działa.

pracowita pszczółka
źródło
Może edytuj drugą odpowiedź?
Ross Attrill
0

Korzystanie z tej divsztuczki z tab_index="0"lub tabIndex="-1"działa, ale za każdym razem, gdy użytkownik skupia się na widoku, który nie jest elementem, otrzymasz brzydki zarys całej witryny. Można to naprawić, ustawiając CSS dla elementu div, który ma być używany outline: nonew fokusie.

Oto implementacja ze stylizowanymi komponentami:

import styled from "styled-components"

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

aw klasie App:

  render() {
    return (      
      <KeyReceiver onKeyDown={this.handleKeyPress} tabIndex={-1}>
          Display stuff...
      </KeyReceiver>
    )
Muppet
źródło
-1

Brakuje powiązania metody w konstruktorze. Tak sugeruje React:

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

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

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

Są na to inne sposoby, ale będzie to najbardziej wydajne w czasie wykonywania.

Iwnnay
źródło
-3

Musisz uniemożliwić wywołanie domyślnego zdarzenia.

onKeyPressed(e) {
   e.preventDefault();
   console.log(e.key);
}
Boluwatife Fakorede
źródło