Michael
Michael

Reputation: 7123

onKeyDown event not working on divs in React

I want to use a keyDown event on a div in React. I do:

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

It works fine, but I would like to do it more in React style. I tried

onKeyDown={this.onKeyPressed}

on the component. But it doesn't react. It works on input elements as I recall.

Codepen

How can I do it?

Upvotes: 177

Views: 229962

Answers (7)

user1503606
user1503606

Reputation: 4300

For anyone else having issues with this I was losing the plot with on key down and on key up not working you can use mouse events.

This was fine for what I needed.

import './App.css'

function App() {
  const handleDown = () => {
    console.log('down')
  }
  const handleUp = () => {
    console.log('up')
  }
  return (
    <button onMouseDown={handleDown} onMouseUp={handleUp}>
      PRESS DOWN
    </button>
  )
}

export default App

Upvotes: 0

Pierre Olivier Tran
Pierre Olivier Tran

Reputation: 1875

Also, remember that this trick will only work when focus is set on your div. If you want to manage keypress as soon as the div pops up, you can use this trick (especially useful for Drawers/Modals)

Upvotes: 7

steel
steel

Reputation: 12570

You're thinking too much in pure Javascript. Get rid of your listeners on those React lifecycle methods and use event.key instead of event.keyCode (because this is not a JS event object, it is a React SyntheticEvent). Your entire component could be as simple as this (assuming you haven't bound your methods in a 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>
  )
}

Upvotes: 11

Muppet
Muppet

Reputation: 6019

Using the div trick with tab_index="0" or tabIndex="-1" works, but any time the user is focusing a view that's not an element, you get an ugly focus-outline on the entire website. This can be fixed by setting the CSS for the div to use outline: none in the focus.

Here's the implementation with styled components:

import styled from "styled-components"

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

and in the App class:

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

Upvotes: 10

burak
burak

Reputation: 4064

You should use tabIndex attribute to be able to listen onKeyDown event on a div in React. Setting tabIndex="0" should fire your handler.

Upvotes: 272

busybee
busybee

Reputation: 194

The answer with

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

works for me, please note that the tabIndex requires a number, not a string, so tabIndex="0" doesn't work.

Upvotes: 5

Panther
Panther

Reputation: 9418

You need to write it this way

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

If onKeyPressed is not bound to this, then try to rewrite it using arrow function or bind it in the component constructor.

Upvotes: 37

Related Questions