Sự kiện onKeyDown không hoạt động trên div trong React


92

Tôi muốn sử dụng sự kiện keyDown trên div trong React. Tôi làm:

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

Nó hoạt động tốt, nhưng tôi muốn làm điều đó nhiều hơn theo phong cách React. Tôi đã thử

        onKeyDown={this.onKeyPressed}

trên thành phần. Nhưng nó không phản ứng. Nó hoạt động trên các yếu tố đầu vào như tôi nhớ lại.

Codepen

Tôi làm nó như thế nào?


Cá nhân tôi thích cách tiếp cận của bạn. Đây có vẻ là một cách tốt để liên kết các nét chính với tài liệu, nằm ngoài phạm vi thành phần của bạn. và không yêu cầu tập trung vào một yếu tố cụ thể.
Daniel

Câu trả lời:


159

Bạn nên sử dụng thuộc tính tabIndex để có thể lắng nghe onKeyDownsự kiện trên một div trong React. Cài đặt tabIndex="0"sẽ kích hoạt trình xử lý của bạn.


2
Đây có vẻ như là một thiết kế xấu, cố chấp. Điều gì sẽ xảy ra nếu tôi muốn xử lý một sự kiện nổi bọt trên một phần tử thùng chứa nhưng bản thân thùng chứa đó không bao giờ được tập trung?
Sẽ P.

1
Đối với tôi, điều này có vẻ giống như một cách hack hoặc giải pháp khác. Có thể có lúc bạn cần div đã cho duy trì thứ tự tab.
Iwnnay

9
tabIndex={-1}cũng có thể được sử dụng nếu bạn đang làm việc với một phần tử không tương tác và không muốn thay đổi thứ tự tab của tài liệu.
IliasT

1
bạn phải đặt tabIndex, để làm cho div có thể lấy tiêu điểm. Bạn có thể đặt tabIndex thành 0,1, -1 .... Nhưng trước khi làm điều đó, hãy kiểm tra điều này trên webaim.org/techniques/keyboard/tabindex Bạn có thể sẽ muốn đặt thành -1 vì bạn đang thao tác nó theo chương trình .
Kavita

vì một số lý do, điều này chỉ hoạt động nếu tôi có một phần tử <input> trong div với tabIndex. Hoặc một cái gì đó khác có thể được lấy nét. Chỉ với các div khác, nó vẫn không thu hút được. Có suy nghĩ gì không?
Flion

24

Bạn cần phải viết nó theo cách này

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

Nếu onKeyPressedkhông bị ràng buộc this, thì hãy thử viết lại nó bằng cách sử dụng hàm mũi tên hoặc ràng buộc nó trong thành phần constructor.


2
Lấy làm tiếc. Tôi chắc chắn, tôi chỉ bỏ qua điều này. Nhưng đây không phải là vấn đề. Nó vẫn không hoạt động.
Michael

câu trả lời cập nhật. Bạn nên nhấp vào div hoặc đưa nó vào tiêu điểm trước khi nhấn bất kỳ phím nào.
Panther

Tôi đã làm. Nó không hoạt động. Tôi đã cập nhật mã ở trên. Nó hoạt động tốt với các lệnh DOM, nhưng không hoạt động với kiểu React.
Michael

bạn có thể giải thích những gì không hoạt động? chức năng của bạn không được gọi hoặc console.logkhông có gì vượt trội ?
Panther

Hàm không được gọi. Tôi có một codepen: codepen.io/lafisrap/pen/OmyBYG . Nó về dòng 275 và 307.
Michael

7

Bạn đang nghĩ quá nhiều về Javascript thuần túy. Loại bỏ trình lắng nghe của bạn trên các phương thức vòng đời React đó và sử dụng event.keythay thế event.keyCode(bởi vì đây không phải là một đối tượng sự kiện JS, nó là một React SyntheticEvent ). Toàn bộ thành phần của bạn có thể đơn giản như thế này (giả sử bạn chưa ràng buộc các phương thức của mình trong một hàm tạo).

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

Đó chính xác là cách tôi muốn viết nó. Nhưng quái, nó không đi đâu. Đây là codepen: codepen.io/lafisrap/pen/OmyBYG . Nếu bạn nhận xét dòng 275 thì đầu vào bảng phím (các phím con trỏ) không hoạt động. onKeyDown ở dòng 307.
Michael

keyCode tôi sử dụng cho các phím con trỏ, vì chúng không trả về ký tự nào.
Michael

1
React không sử dụng các đối tượng sự kiện Javascript, vì vậy không có thuộc tính keyCode. eventĐối tượng đó là một React SyntheticEvent .
thép

@Michael Tôi cũng không hoàn toàn chắc chắn về cách bạn đang kích hoạt một sự kiện quan trọng trên div, nhưng khi tôi đặt mã này vào một trò chơi với một phần mềm onClickchống đỡ thay vì onKeyDowntrình xử lý sẽ kích hoạt.
thép

Cảm ơn. Điều đó giải thích nó ... Các sự kiện chính hoạt động trong các trường đầu vào.
Michael

2

Câu trả lời với

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

hoạt động đối với tôi, xin lưu ý rằng tabIndex yêu cầu một số, không phải một chuỗi, vì vậy tabIndex = "0" không hoạt động.


Có thể chỉnh sửa câu trả lời khác?
Ross Attrill

0

Sử dụng divthủ thuật có tab_index="0"hoặc hiệu tabIndex="-1"quả, nhưng bất cứ khi nào người dùng tập trung vào một chế độ xem không phải là một phần tử, bạn sẽ nhận được một đường viền tiêu điểm xấu xí trên toàn bộ trang web. Điều này có thể được khắc phục bằng cách đặt CSS cho div để sử dụng outline: nonetrong tiêu điểm.

Đây là cách triển khai với các thành phần được tạo kiểu:

import styled from "styled-components"

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

và trong lớp Ứng dụng:

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

-1

Bạn đang thiếu ràng buộc của phương thức trong hàm tạo. Đây là cách React đề nghị bạn làm điều đó:

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

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

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

Có nhiều cách khác để làm điều này, nhưng cách này sẽ hiệu quả nhất trong thời gian chạy.


-3

Bạn phải ngăn sự kiện mặc định kích hoạt.

onKeyPressed(e) {
   e.preventDefault();
   console.log(e.key);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.