单击时,React会阻止嵌套组件中的事件冒泡

时间:2016-07-27 17:51:19

标签: reactjs onclick event-propagation

这是一个基本组件。 <ul><li>都有onClick函数。我只想点击<li>上的onClick,而不是<ul>。我怎样才能做到这一点?

我玩e.preventDefault(),e.​​stopPropagation(),但无济于事。

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

  handleClick() {
    // do something
  }

  render() {

    return (
      <ul 
        onClick={(e) => {
          console.log('parent');
          this.handleClick();
        }}
      >
        <li 
          onClick={(e) => {
            console.log('child');
            // prevent default? prevent propagation?
            this.handleClick();
          }}
        >
        </li>       
      </ul>
    )
  }
}

// => parent
// => child

9 个答案:

答案 0 :(得分:89)

我有同样的问题。我发现stopPropagation 做了工作。我将列表项拆分为一个单独的组件,如下所示:

class List extends React.Component {
  handleClick = e => {
    // do something
  }

  render() {
    return (
      <ul onClick={this.handleClick}>
        <ListItem onClick={this.handleClick}>Item</ListItem> 
      </ul>
    )
  }
}

class ListItem extends React.Component {
  handleClick = e => {
    e.stopPropagation();
    this.props.onClick();
  }

  render() {
    return (
      <li onClick={this.handleClick}>
        {this.props.children}
      </li>       
    )
  }
}

答案 1 :(得分:39)

React使用事件委托在文档上使用单个事件侦听器来处理冒泡的事件,例如本例中的“click”,这意味着无法停止传播;当你在React中与它交互时,真实事件已经传播了。可以在React的合成事件上使用stopPropagation,因为React在内部处理合成事件的传播。

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
}

答案 2 :(得分:19)

按照DOM事件的顺序:捕获与冒泡

事件传播有两个阶段。这些被称为“捕获” “冒泡”

               | |                                   / \
---------------| |-----------------   ---------------| |-----------------
| element1     | |                |   | element1     | |                |
|   -----------| |-----------     |   |   -----------| |-----------     |
|   |element2  \ /          |     |   |   |element2  | |          |     |
|   -------------------------     |   |   -------------------------     |
|        Event CAPTURING          |   |        Event BUBBLING           |
-----------------------------------   -----------------------------------

捕获阶段首先发生,然后是冒泡阶段。当您使用常规DOM api注册事件时,默认情况下,这些事件将成为冒泡阶段的一部分,但这可以在事件创建时指定

// CAPTURING event
button.addEventListener('click', handleClick, true)

// BUBBLING events
button.addEventListener('click', handleClick, false)
button.addEventListener('click', handleClick)

在React中,冒泡事件也是您默认使用的事件。

// handleClick is a BUBBLING (synthetic) event
<button onClick={handleClick}></button>

// handleClick is a CAPTURING (synthetic) event
<button onClickCapture={handleClick}></button>

让我们看一下我们的handleClick回调(反应):

function handleClick(e) {
  // This will prevent any synthetic events from firing after this one
  e.stopPropagation()
}
function handleClick(e) {
  // This will set e.defaultPrevented to true
  // (for all synthetic events firing after this one)
  e.preventDefault()  
}

我这里没有提到的另一种选择

如果您在所有事件中都调用e.preventDefault(),则可以检查事件是否已被处理,并防止再次处理该事件:

handleEvent(e) {
  if (e.defaultPrevented) return  // Exits here if event has been handled
  e.preventDefault()

  // Perform whatever you need to here.
}

有关合成事件和本地事件之间的区别,请参见React文档:https://reactjs.org/docs/events.html

答案 3 :(得分:1)

这不是100%理想的,但是如果让孩子传承props太麻烦了->儿童时尚或创建Context.Provider / Context.Consumer 只是),而您正在处理另一个库,该库具有在您的库之前运行的自己的处理程序,您也可以尝试:

   function myHandler(e) { 
        e.persist(); 
        e.nativeEvent.stopImmediatePropagation();
        e.stopPropagation(); 
   }

据我了解,event.persist方法可防止将对象立即扔回到React的SyntheticEvent池中。因此,因此,在您到达React时传递的event实际上并不存在!之所以会发生这种情况,是因为在React中,React在内部首先通过检查SyntheticEvent处理程序的状态来处理内部事情(尤其是如果父级有回调)。

只要您没有调用persist就会产生大量内存来继续创建诸如onMouseMove之类的事件(并且您没有创建某种Cookie Clicker游戏,例如奶奶的Cookie),应该很好!

另外请注意:从偶尔阅读他们的GitHub来,我们应该继续关注React的未来版本,因为他们可能最终解决了一些痛苦,因为他们似乎正致力于将React代码折叠到编译器/编译器中。

答案 4 :(得分:1)

这是防止click事件前进到下一个组件然后调用yourFunction的简便方法。

<Button onClick={(e)=> {e.stopPropagation(); yourFunction(someParam)}}>Delete</Button>

答案 5 :(得分:0)

我在使event.stopPropagation()工作时遇到了问题。如果您也这样做,请尝试将其移至点击处理程序函数的顶部,这是我阻止事件冒泡所需的操作。函数示例:

  toggleFilter(e) {
    e.stopPropagation(); // If moved to the end of the function, will not work
    let target = e.target;
    let i = 10; // Sanity breaker

    while(true) {
      if (--i === 0) { return; }
      if (target.classList.contains("filter")) {
        target.classList.toggle("active");
        break;
      }
      target = target.parentNode;
    }
  }

答案 6 :(得分:0)

您可以通过检查事件目标来避免事件冒泡。
例如,如果您将输入嵌套到div元素中,而该元素具有单击事件的处理程序,并且您不想处理该事件,则在单击输入时,可以将event.target传递到处理程序中,然后检查是否为处理程序应该根据target的属性执行。
例如,您可以检查if (target.localName === "input") { return}
因此,这是一种“避免”处理程序执行的方法

答案 7 :(得分:0)

解决此问题的另一种方法可能是在孩子上设置 onMouseEnter 和 onMouseLeave 事件。 (你的

  • 标签) 每当鼠标悬停在孩子上时,您都可以设置一个特定状态,该状态将停止父级执行 onClick 函数内容。 类似的东西:

    class List extends React.Component {
      constructor(props) {
        super(props)
        this.state.overLi = false
      }
    
      handleClick() {
        // do something
      }
    
      render() {
        return (
          <ul
            onClick={(e) => {
              if (!this.state.overLi) {
                console.log("parent")
                this.handleClick()
              }
            }}
          >
            <li
              onClick={(e) => {
                console.log("child")
                // prevent default? prevent propagation?
                this.handleClick()
              }}
              onMouseEnter={() =>
                this.setState({
                  overLi: true,
                })
              }
              onMouseLeave={() =>
                this.setState({
                  overLi: false,
                })}
            ></li>
          </ul>
        )
      }
    }
    

    我进行了大量搜索,但没有设法使用 e.stopPropagation() 为我的上下文实现任何解决方案

  • 答案 8 :(得分:-2)

    执行此操作的新方法要简单得多,并且可以节省您一些时间!只需将事件传递到原始点击处理程序中,然后调用preventDefault();

    clickHandler(e){
        e.preventDefault();
        //Your functionality here
    }