Reputation: 2993
I want to bind some hotkeys to a div: Whenever the user clicks somewhere inside the div and then presses the S
key I want to console.log
something. However, I don't want this hotkey to be global and to be triggered each and every time the user presses S
.
Here is what I've got so far:
import React from "react"
import Mousetrap from "mousetrap"
export default class Mouse extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
let form = document.querySelector("form")
let m = new Mousetrap(form)
m.bind("s", () => {
console.log("s")
})
}
componentWillUnmount() {
// m.unbind("s", () => {
// console.log("s")
// })
}
render() {
return (
<form
style={{ width: "300px", height: "300px", backgroundColor: "pink" }}
>
<input type="text" />
</form>
)
}
}
The Mousetrap docs say that I can bind my mousetrap like so:
var form = document.querySelector('form');
var mousetrap = new Mousetrap(form);
mousetrap.bind('mod+s', _handleSave);
mousetrap.bind('mod+z', _handleUndo);
As you can see in my example above, that's what I've done. It does work in this sense: whenever I type S
while I'm in the input
of the form, the console.log
is being triggered. However, I don't want to use a form and neither do I want my user to be inside an input: I just want my user to have clicked on the div
. I cannot get this to work though. I would expect it to look something like this:
import React from "react"
import Mousetrap from "mousetrap"
export default class Mouse extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
let form = document.querySelector(".trigger")
let m = new Mousetrap(form)
m.bind("s", () => {
console.log("s")
})
}
componentWillUnmount() {
// m.unbind("s", () => {
// console.log("s")
// })
}
render() {
return (
<div
className="trigger"
style={{ width: "300px", height: "300px", backgroundColor: "pink" }}
>
Click me!
</div>
)
}
}
However, this doesn't work. Nothing is being triggered.
Edit: Also, one thing I don't quite understand in the first example above is that I am binding Mousetrap to form
. However, the s
hotkey is only ever triggered when I am inside the input field
of form
, but never when I just click on the form
but not the input
.
Upvotes: 0
Views: 510
Reputation: 16344
The reason this happens is that the Mousetrap
is checking if the element is focused. div
s (or in fact any other block element like a form
) can only be focused if they have a tabindex
defined. Inputs can be focused without that.
But I believe you do not need to explicitly bind the Mousetrap
to the div
at all. All you need to do is to track the active state of your div
and bind()
or unbind()
the trap accordingly.
Example:
class Mouse extends Component {
state = {
active: false,
}
constructor(props) {
super(props);
this.trap = new Mousetrap();
}
handleClick = () => {
this.setState(({active}) => {
if (!active) {
this.trap.bind('s', this.handleKeyPress);
} else {
this.trap.unbind('s');
}
return {active: !active}
})
}
handleKeyPress = () => {
console.log('User pressed S.')
}
componentWillUnmount() {
this.trap.reset();
}
render() {
const {active} = this.state;
return (
<div
className={cN('trigger', {active})}
onClick={this.handleClick}
>
Click me!
</div>
);
}
}
Demo:
Upvotes: 2
Reputation: 2993
I think I've found the solution. Giving the div
a tabindex
makes it selectable, and thus, the hot key will be registered. I am not sure why this is, whether it's strictly necessary or a bit of a hacky solution. So all I had to do is:
<div className="trigger" tabindex="0">
...if anyone has a better explanation that goes in some more depth, feel free to post still, as I won't select this as the final answer for now.
Upvotes: 0