I want to handle changes by keyboard differently from changes by mouse clicks. For this purpose I have created following component. My problem is, that when I use the arrow keys and space to select the radio button, it uses the onClick method from the parent div element before it uses the onChange method from the input element. What is the reason for this behaviour and how can I handle it correctly?
class Radio extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onClick = this.onClick.bind(this);
this.state = { checked: false };
}
onChange() {
this.setState({ checked: !this.state.checked });
console.log('onChange');
}
onClick() {
this.onChange();
console.log('onClick');
}
render() {
return (
<div onClick={this.onClick}>
<input
type="radio"
checked={this.state.checked}
onChange={this.onChange}
/>
Click me!
</div>
);
}
}
ReactDOM.render(<Radio />, document.getElementById('root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
As seen in this issue and this part of the W3C documentation, this seems to be a default browser behavior.
When the user triggers an element with a defined activation behavior in a manner other than clicking it, the default action of the interaction event must be to run synthetic click activation steps on the element.
To work around this, add the following logic in your onClick handler:
if (e.type === 'click' && e.clientX !== 0 && e.clientY !== 0) {
// This is a real click. Do something here
}
Creds to GitHub user Ro Savage, for pointing this out.
Here's a full demo for you:
class Radio extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onClick = this.onClick.bind(this);
this.state = { checked: false };
}
onChange() {
this.setState({ checked: !this.state.checked });
console.log('onChange');
}
onClick(e) {
if (e.type === 'click' && e.clientX !== 0 && e.clientY !== 0) {
this.onChange();
console.log('onClick');
} else {
console.log('prevented "onClick" on keypress');
}
}
render() {
return (
<div onClick={this.onClick}>
<input
type="radio"
checked={this.state.checked}
onChange={this.onChange}
/>
Click me!
</div>
);
}
}
ReactDOM.render(<Radio />, document.getElementById('root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With