【问题标题】:How can I prevent event bubbling in nested React components on click?如何防止单击时嵌套 React 组件中的事件冒泡?
【发布时间】:2016-12-01 21:05:11
【问题描述】:

这是一个基本组件。 <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

【问题讨论】:

标签: reactjs onclick event-propagation


【解决方案1】:

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

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();  //  <------ Here is the magic
    this.props.onClick();
  }

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

【讨论】:

  • ListItem 组件中的this.props.handleClick() 不应该是this.props.click吗?
  • 除了stopPropogation还有其他方法吗?
  • &lt;Link&gt;这样的原生组件呢
  • 如果需要给handleClick传参数怎么办?
  • 这不是回答错误的问题吗? OP 要求 ListItem 处理该事件。该解决方案有 List 处理它。 (除非我误解了什么。)
【解决方案2】:

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

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

【讨论】:

  • 超级有用.. stopPropagation() 本身无法正常工作
  • 这对我有用,因为我使用的是 document.addEventListener('click') 和 react 的 onClick
  • 有时这可能不起作用——例如,如果您使用像 Material-UI (www.material-ui.com) 这样的库,或者将您的处理程序包装在另一个组件的回调中。但是如果直接自己的代码就好了!
  • @rob2d,那么在使用Material-UI时如何处理呢?
  • @Italik 上面有一个示例函数;但是您需要立即将其作为回调中的第一件事调用,以免它被虚拟化/吞噬。至少AFAIK。谢天谢地,最近还没有和这个作斗争。
【解决方案3】:

按 DOM 事件的顺序:CAPTURING vs BUBBLING

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

               | |                                   / \
---------------| |-----------------   ---------------| |-----------------
| 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 回调(React)内部:

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

【讨论】:

  • 我只想说,除了我使用onClickCapture 时,没有任何效果。然后使用event.stopProgatation()event.stopImmediatePropagation() 终于奏效了。 未来开发者注意事项 - 如果有需要避免点击处理程序的 3rd 方包,请执行上述操作,它会阻止它触发。否则,如果你使用onClick,它不会给你你真正需要的结果。
  • 这应该是公认的答案。你真的不应该使用 stopPropagation。我强烈推荐阅读这篇文章:css-tricks.com/dangers-stopping-event-propagation
【解决方案4】:

这是一种防止点击事件前进到下一个组件然后调用 yourFunction 的简单方法。

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

【讨论】:

  • 优秀。停止在嵌套子项中传播 ContextMenu 事件时起作用
  • 太棒了!当单击按钮时,我在表单标题中使用了这个 AND onSubmit 来执行两个不同的功能。想听听 cmets 对这种做法的看法。
【解决方案5】:

这不是 100% 理想的,但如果将 props 传递给儿童太痛苦 -> 儿童时尚或创建 Context.Provider/Context.Consumer 只是为此目的),并且您正在处理另一个库,它有自己的处理程序,它在您之前运行,您也可以尝试:

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

据我了解,event.persist 方法可以防止对象立即被扔回 React 的 SyntheticEvent 池中。所以正因为如此,在 React 中传递的 event 实际上在你到达它的时候并不存在!这发生在孙辈中,因为 React 在内部处理事情的方式是首先检查父级是否有 SyntheticEvent 处理程序(特别是如果父级有回调)。

只要您不调用persist 来创建大量内存以继续创建诸如onMouseMove 之类的事件(并且您不是在创建某种Cookie Clicker 游戏,例如Grandma's Cookies),它应该是完美的很好!

另请注意:通过偶尔阅读他们的 GitHub,我们应该密切关注 React 的未来版本,因为他们可能最终解决了一些痛苦,因为他们似乎正在努力制造在编译器/转译器中折叠 React 代码。

【讨论】:

    【解决方案6】:

    如果您希望嵌套元素中的动作而不是父元素中的动作发生,那么,您可以从父元素的动作处理程序中检查目标的类型,然后根据该类型执行动作,也就是说,如果目标是我们的嵌套元素,我们什么也不做。否则两个处理程序都会被调用。

    // Handler of the parent element. Let's assume the nested element is a checkbox
    function handleSingleSelection(e) {
        if(e.target.type !== 'checkbox') {
            // We do not do anything from the 
            // parent handler if the target is a checkbox ( our nested element)
            // Note that the target will always be the nested element
            dispatch(lineSelectionSingle({ line }))
        }
    }
    

    【讨论】:

    • 这个答案可能对每个人都没有用,但对我来说是! +1
    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        解决此问题的另一种方法是在子级上设置 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() 为我的上下文实施任何解决方案

      • 【讨论】:

          【解决方案10】:

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

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

          【讨论】:

          • -1;我对此进行了测试,但它不适用于点击传播。 AFAIK 如果您想要一个用于停止基本链接功能的链接的 onClick 处理程序,但对于事件冒泡,这将很有用。
          猜你喜欢
          • 1970-01-01
          • 2015-12-10
          • 1970-01-01
          • 2014-07-26
          • 2014-09-14
          • 2012-12-18
          • 2023-03-21
          • 2019-11-21
          相关资源
          最近更新 更多