Событие onKeyDown не работает с div в React


92

Я хочу использовать событие keyDown для div в React. Я делаю:

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

Работает нормально, но хотелось бы больше в стиле React. Я старался

        onKeyDown={this.onKeyPressed}

на компоненте. Но не реагирует. Насколько я помню, он работает с элементами ввода.

Codepen

Как я могу это сделать?


Лично мне нравится ваш подход. Похоже, это хороший способ привязать нажатия клавиш к документу, который выходит за рамки вашего компонента. и не требует акцента на конкретном элементе.
Daniel

Ответы:


159

Вы должны использовать атрибут tabIndex, чтобы иметь возможность прослушивать onKeyDownсобытие в div в React. Настройка tabIndex="0"должна запустить ваш обработчик.


2
Это кажется плохим самоуверенным дизайном. Что, если я хочу обработать всплывающее событие в элементе контейнера, но сам этот контейнер никогда не должен быть сфокусирован?
Уилл П.

1
Для меня это больше похоже на взлом или обходной путь. Может быть время, когда вам нужно, чтобы данный div поддерживал порядок табуляции.
Iwnnay

11
tabIndex={-1}также может использоваться, если вы работаете с элементом, который не является интерактивным, и не хотите изменять порядок табуляции в документе.
IliasT

1
вам нужно установить tabIndex, чтобы сделать div фокусируемым. Вы можете установить tabIndex на 0,1, -1 .... Но прежде чем вы это сделаете, проверьте webaim.org/techniques/keyboard/tabindex. Вероятно, вы захотите установить значение -1, поскольку вы управляете им программно. .
Кавита

по какой-то причине это работает, только если у меня есть элемент <input> в div с tabIndex. Или что-то еще, на чем можно сосредоточиться. Только с другими div он все еще не захватывается. есть идеи?
Flion

24

Вам нужно написать это так

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

Если onKeyPressedне привязан this, попробуйте переписать его с помощью стрелочной функции или привязать к компоненту constructor.


2
Сожалею. Я был уверен, я просто не заметил этого. Но проблема не в этом. Это все еще не работает.
Майкл

обновленный ответ. Вы должны либо нажать на div, либо переместить его в фокус, прежде чем нажимать какие-либо клавиши.
Пантера

Я сделал. Это не работает. Я обновил код выше. Он отлично работает с командами DOM, но не в стиле React.
Майкл

можешь объяснить, что не работает? ваша функция не вызывается или console.logничего не выводит?
Пантера

Функция не вызывается. У меня есть код: codepen.io/lafisrap/pen/OmyBYG . Речь
Майкл

7

Вы слишком много думаете на чистом Javascript. Избавьтесь от слушателей этих методов жизненного цикла React и используйте event.keyвместо них event.keyCode(потому что это не объект события JS, это React SyntheticEvent ). Весь ваш компонент может быть таким же простым (при условии, что вы не связали свои методы в конструкторе).

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

Именно так я и хотел это написать. Но, черт возьми, не пойдет. Вот код: codepen.io/lafisrap/pen/OmyBYG . Если вы прокомментируете строку 275, значит, ввод с клавиатуры (клавиши курсора) не работает. onKeyDown находится в строке 307.
Майкл

keyCode я использую для клавиш курсора, так как они не возвращают никаких символов.
Майкл

1
React не использует объекты событий Javascript, поэтому свойство keyCode отсутствует. Этот eventобъект - React SyntheticEvent .
Steel

@Michael Я также не совсем уверен, как вы запускаете ключевое событие в div, но когда я помещаю этот код в скрипт с onClickопорой вместо onKeyDownсрабатывания обработчика.
Steel

Спасибо. Это объясняет ... Однако ключевые события работают в полях ввода.
Майкл

2

Ответ с

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

у меня работает, обратите внимание, что tabIndex требует числа, а не строки, поэтому tabIndex = "0" не работает.


Может быть, отредактировать другой ответ?
Росс Аттрилл

1

Использование divтрюка с tab_index="0"или tabIndex="-1"работает, но каждый раз, когда пользователь фокусирует внимание на представлении, которое не является элементом, вы получаете уродливую схему фокуса на всем веб-сайте. Это можно исправить, установив CSS для div, который будет использоваться outline: noneв фокусе.

Вот реализация со стилизованными компонентами:

import styled from "styled-components"

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

и в классе App:

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

-1

Вам не хватает привязки метода в конструкторе. Вот как React предлагает вам это сделать:

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

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

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

Есть и другие способы сделать это, но во время выполнения это будет наиболее эффективным.


-3

Вы должны предотвратить запуск события по умолчанию.

onKeyPressed(e) {
   e.preventDefault();
   console.log(e.key);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.