如何防止点击时嵌套 React 组件中的事件冒泡?

问题描述 投票:0回答: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
11个回答
298
投票

我也有同样的问题。我发现 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>       
    )
  }
}

103
投票

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

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

73
投票

关于 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


17
投票

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

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

6
投票

这不是 100% 理想,但如果在儿童中传递

props
太痛苦 -> 儿童时尚或为此目的创建
Context.Provider
/
Context.Consumer
just),你正在处理另一个库,它有自己的处理程序,它在您的处理程序之前运行,您也可以尝试:

   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 代码/转译器。


5
投票

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

// 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
投票

我在

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

1
投票

您可以通过检查事件目标来避免事件冒泡。
例如,如果您将输入嵌套到 div 元素,其中有单击事件的处理程序,并且您不想处理它,则单击输入时,您可以将

event.target
传递到您的处理程序中,并检查处理程序应该是根据目标的属性执行。
例如,您可以检查
if (target.localName === "input") { return}

所以,这是一种“避免”处理程序执行的方法


0
投票

解决此问题的另一种方法可能是在子级上设置 onMouseEnter 和 onMouseLeave 事件。 (您的 < li > 标签) 每当鼠标悬停在子级上时,您都可以设置一个特定的状态,以阻止父级执行 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() 为我的上下文实现任何解决方案


0
投票

您可以验证点击的元素是否是预期的元素。

例如:

 class List extends React.Component {   
             constructor(props) {
                     super(props);   
             }   
             handleClick(e) {
                  if(e.target.className==='class-name'){
                      // do something
                  }   
             }
             render() {
     
                 return (
                     <ul 
                          {/* Replace this with handleClick */}
                          onClick={(e) => {
                          console.log('parent');
                          this.handleClick();
                        }}
    
                    <li 
                       onClick={(e) => {
                          console.log('child');
                          // prevent default? prevent propagation?
                          this.handleClick();
                       }}
    
                    </li>       
                 </ul>
       )   } }

-4
投票

执行此操作的新方法更加简单,并且会节省您一些时间!只需将事件传递到原始点击处理程序并调用

preventDefault();

clickHandler(e){
    e.preventDefault();
    //Your functionality here
}
© www.soinside.com 2019 - 2024. All rights reserved.