React中单选按钮的onChange事件触发了父级div元素的onClick事件

3
我希望通过键盘和鼠标点击不同地处理更改。为此,我创建了以下组件。我的问题是,当我使用箭头键和空格选择单选按钮时,它会先使用父div元素的onClick方法,然后才使用输入元素的onChange方法。这种行为的原因是什么,如何正确处理它?

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>

3个回答

4
这个问题所示以及W3C文档的这一部分,这似乎是浏览器的默认行为。

当用户以非点击方式触发具有定义的激活行为的元素时,交互事件的默认操作必须是在该元素上运行合成点击激活步骤。

为了解决此问题,请在您的onClick处理程序中添加以下逻辑:

if (e.type === 'click' && e.clientX !== 0 && e.clientY !== 0) {
  // This is a real click. Do something here
}

感谢 GitHub 用户 Ro Savage 指出此事。


这里有一个完整的演示:

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>


1
非常有趣!你的解决方案非常好,谢谢。 - user5520186

2
您可以禁用点击事件的传播:
<input
    type="radio"
    checked={this.state.checked}
    onClick={(e)=>{e.stopPropagation()}}
    onChange={this.onChange}
/>

谢谢您的回答。这个解决方案的问题是,当使用键盘时,单选按钮在视觉上不会被选中。有没有什么办法来处理这个问题? - user5520186

0
事件的顺序很重要:点击事件会在更改事件之前触发。 由于事件会冒泡,因此父元素的 onClick 处理程序将在子元素的 onChange 处理程序之前被触发。
为了解决您的特定问题,例如,您可以侦听容器 div 上的 keydown 事件。这个事件应该会在点击或更改事件之前被触发。

class Radio extends React.Component {
  constructor(props) {
    super(props);

    this.onChange = this.onChange.bind(this);
    this.onKeyUp = this.onKeyUp.bind(this);

    this.state = { checked: false };
  }

  onChange() {
    this.setState({ checked: !this.state.checked });
    console.log('onChange');
  }

  onKeyUp() {
    console.log('onKeyUp');
    this.onChange();
  }

  render() {
    return (
      <div onKeyUp={this.onKeyUp}>
        <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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接