10

I'm trying to add an event listener for keydown event in an image (or div) tag. It works if I add it to the document with document.addEventListener, but it doesn't when I try to put it into the specific element that I create in react (I noted in the code what works and what doesn't). Also handleClick works and handleKey does not, no matter which format I put it into the tag with.

class PrescriptionImage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      patient: "",
      rotation: 0
    };
    this.handleKey = this.handleKey.bind(this);
  }

  handleClick() {
    this.setState({rotation: this.state.rotation + 270})
  }

  handleKey(e) {
    e.preventDefault();
    console.log(e);
    if (e.code == 'ArrowLeft') {
      if (e.ctrlKey) {
        this.setState({rotation: this.state.rotation + 270})
      }
    }
  }

  componentDidMount() {
//    document.getElementById("left").addEventListener("keydown", this.handleKey, true); This doesn't work (no error)
//    this.RxImage.addEventListener("keydown", this.handleKey, false); This doesn't work, (can't find addEventListener of "undefined")
//    document.addEventListener("keydown", this.handleKey, false); This works.
    fetch("http://localhost:3333/patientAddress.json")
      .then(res => res.json())
      .then(
        result => {
          this.setState({
            isLoaded: true,
            patient: result.order.patient
          });
        },
        error => {
          this.setState({
            isLoaded: true,
            error
          });
        }
      );
  }

  componentWillUnmount(){
    document.removeEventListener("keydown", this.handleKey, false);
  }

  render() {
    const { error, isLoaded, patient, rotation } = this.state;
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (!isLoaded) {
      return <div>Loading...</div>;
    } else {
      return <img className="prescription-image" style={{width: "98%", height: "98%", transform: `rotate(${rotation}deg)`}} src={"data:image/png;base64," + patient.rx.imageData} onClick={() => this.handleClick()} onKeyDown={this.handleKey} />;
    }
  }
}

ReactDOM.render(<PrescriptionImage />, document.getElementById("left"));
4
  • 1
    The event is called onKeyDown (camelcase). Try renaming your prop: reactjs.org/docs/events.html#keyboard-events Commented Dec 21, 2017 at 22:10
  • Weird, I copied that from stackoverflow something. Changing it to the correct case didn't change anything (no error, doesn't work), going to change it in the question Commented Dec 21, 2017 at 22:40
  • try adding a ref to the img or div: ref={ (el) => {this.handleKey = el }} then in handleKey(e) 'if e.key === <key_code> Commented Dec 21, 2017 at 22:45
  • Tried ref in the 2nd commented line there.... gives me this error: ` Uncaught TypeError: Cannot read property 'addEventListener' of undefined`.... If I leave the line commented out I get no error, doesn't work. (I had previously added ref={(RxImage) => {this.RxImage = this}}, but your version also doesn't work. Commented Dec 21, 2017 at 22:54

2 Answers 2

4

You got 2 main issues here:

  1. The keyDown event needs the div to be in focus. one way to do it is to add a tabindex attribute to the div. after you focus on it you can trigger the onKeyDown event on any key on the keyboard.
  2. In your handler you are trying to check for e.code but in fact the correct property is e.keycode.
    with that said, you should carefully read about the browsers support for it as it is considered as deprecated at some browsers.
    Here is a list of the available properties of this event and their status (check key for example).

EDIT
I have added another approach, using the ref API of react. This way you can attach an event listener the way you did before and also trigger a focus via code (see componentDidMount).

Here is a running example:

class App extends React.Component {

  componentDidMount() {
    this.myDiv.addEventListener('keydown', this.handleKey);
    this.myDiv.focus();
  }

  componentWillUnmount() {
    this.myDiv.removeEventListener('keydown', this.handleKey);
  }

  handleKey = e => {
    console.log(e.keyCode);
  }

  render() {
    return (
      <div>
        <div tabIndex="0" onKeyDown={this.handleKey}>click me</div>
        <div tabIndex="1" ref={ref => this.myDiv = ref}>by ref</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Sign up to request clarification or add additional context in comments.

5 Comments

I checked the event object and code actually had the right string set (it works if i document.addEventListener).
yeah as i mentioned you need to focus on the element in order for it to work. you can do it with the mouse or tab etc. as for the code property you are using, that is strange i don't see it in the list. i would stick to the supported and official properties. check the links i attached.
@xyious I've added another example using ref. this way you can trigger a focus with code.
Funny thing, I can't actually make your code work. I keep getting this error: Uncaught TypeError: Cannot read property 'addEventListener' of undefined I'm using babel-core 5.8.23, tried with your react version.
are you calling it inside a native react life-cycle method or your own? if its your own method handler, don't forget to bind it to the class instance
1

This implementation worked well for me.

class App extends React.Component {

  constructor(props) {
    super(props);
    this.myDiv = React.createRef();
  }

  componentDidMount() {
    this.myDiv.current.addEventListener('keydown', this.handleKey);
    this.myDiv.current.focus();
  }

  componentWillUnmount() {
    this.myDiv.current.removeEventListener('keydown', this.handleKey);
  }

  handleKey = e => {
    console.log(e.keyCode);
  }

  render() {
    return (
      <div>
        <div tabIndex="0" onKeyDown={this.handleKey}>click me</div>
        <div tabIndex="1" ref={this.myDiv}>by ref</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.