如何防止点击时嵌套 React 组件中的事件冒泡?
How can I prevent event bubbling in nested React components on click?
这是一个基本组件。 <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
我遇到了同样的问题。我发现 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>
)
}
}
React 使用事件委托和文档上的单个事件侦听器来处理冒泡的事件,如本例中的 'click',这意味着停止传播是不可能的;当您在 React 中与它交互时,真实事件已经传播。 React 的合成事件上的 stopPropagation 是可能的,因为 React 在内部处理合成事件的传播。
stopPropagation: function(e){
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}
执行此操作的新方法要简单得多,而且会节省您一些时间!只需将事件传递到原始点击处理程序并调用 preventDefault();
.
clickHandler(e){
e.preventDefault();
//Your functionality here
}
我在 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;
}
}
这不是 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
处理程序(特别是如果父级有回调)。
只要您不在会创建大量内存以继续创建诸如 onMouseMove
之类的事件上调用 persist
(并且您不是在创建某种 Cookie Clicker 游戏,例如 Grandma's Cookies ), 应该完全没问题!
另请注意:通过阅读他们的 GitHub 偶尔,我们应该留意 React 的未来版本,因为他们 可能 最终解决一些痛苦这是因为他们似乎打算在 compiler/transpiler.
中制作折叠 React 代码
您可以通过检查事件目标来避免事件冒泡。
例如,如果您将输入嵌套到具有点击事件处理程序的 div 元素,并且您不想处理它,那么当输入被点击时,您可以将 event.target
传递到您的处理程序中并检查是否应根据目标的属性执行处理程序。
例如,您可以检查 if (target.localName === "input") { return}
.
所以,这是 "avoid" 处理程序执行
的一种方式
按 DOM 事件的顺序:捕获与冒泡
事件传播有两个阶段。这些被称为 "capturing" 和 "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
这是防止点击事件前进到下一个组件然后调用您的函数的简单方法。
<Button onClick={(e)=> {e.stopPropagation(); yourFunction(someParam)}}>Delete</Button>
解决此问题的另一种方法是在 child 上设置 onMouseEnter 和 onMouseLeave 事件。 (你的
标签)
每当鼠标悬停在 child 上时,您可以设置一个特定的状态,该状态将停止 parent 以执行 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()
为我的上下文实施任何解决方案
如果您希望发生嵌套元素中的动作而不是父元素中的动作,那么,您可以从父元素的动作处理程序中检查目标的类型,然后根据也就是说,如果目标是我们的嵌套元素,我们什么都不做。否则两个处理程序都将被调用。
// 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 }))
}
}
这是一个基本组件。 <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
我遇到了同样的问题。我发现 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>
)
}
}
React 使用事件委托和文档上的单个事件侦听器来处理冒泡的事件,如本例中的 'click',这意味着停止传播是不可能的;当您在 React 中与它交互时,真实事件已经传播。 React 的合成事件上的 stopPropagation 是可能的,因为 React 在内部处理合成事件的传播。
stopPropagation: function(e){
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}
执行此操作的新方法要简单得多,而且会节省您一些时间!只需将事件传递到原始点击处理程序并调用 preventDefault();
.
clickHandler(e){
e.preventDefault();
//Your functionality here
}
我在 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;
}
}
这不是 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
处理程序(特别是如果父级有回调)。
只要您不在会创建大量内存以继续创建诸如 onMouseMove
之类的事件上调用 persist
(并且您不是在创建某种 Cookie Clicker 游戏,例如 Grandma's Cookies ), 应该完全没问题!
另请注意:通过阅读他们的 GitHub 偶尔,我们应该留意 React 的未来版本,因为他们 可能 最终解决一些痛苦这是因为他们似乎打算在 compiler/transpiler.
中制作折叠 React 代码您可以通过检查事件目标来避免事件冒泡。
例如,如果您将输入嵌套到具有点击事件处理程序的 div 元素,并且您不想处理它,那么当输入被点击时,您可以将 event.target
传递到您的处理程序中并检查是否应根据目标的属性执行处理程序。
例如,您可以检查 if (target.localName === "input") { return}
.
所以,这是 "avoid" 处理程序执行
按 DOM 事件的顺序:捕获与冒泡
事件传播有两个阶段。这些被称为 "capturing" 和 "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
这是防止点击事件前进到下一个组件然后调用您的函数的简单方法。
<Button onClick={(e)=> {e.stopPropagation(); yourFunction(someParam)}}>Delete</Button>
解决此问题的另一种方法是在 child 上设置 onMouseEnter 和 onMouseLeave 事件。 (你的
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()
为我的上下文实施任何解决方案如果您希望发生嵌套元素中的动作而不是父元素中的动作,那么,您可以从父元素的动作处理程序中检查目标的类型,然后根据也就是说,如果目标是我们的嵌套元素,我们什么都不做。否则两个处理程序都将被调用。
// 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 }))
}
}