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ć?


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.


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.


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

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.


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

-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.


-3

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

onKeyPressed(e) {
   e.preventDefault();
   console.log(e.key);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.