如何在处理嵌套组件时停止触发 'Click' 事件
How to stop 'Click' event from being triggered when dealing with nested components
我想做什么:
- 创建一个 'resize' angular 指令,允许用户拖动 DOM 元素的边框并调用自定义应用程序逻辑(完成)
- 确保点击不会冒泡到父组件。
stackblitz link -> stackblitz example
组件层次结构是这样的
- app.component>parent.component>child.component(子组件使用'resize'指令)
在'resize.directive'。
- mousedown事件被捕获,
event.stopPropagation()
&event.preventDefault()
用于防止事件冒泡
- mousedown 事件只有当用户的鼠标在边界上时才会被捕获
child.component
- 是一个简单的 div 使用 'resize' 指令
父组件
- 使用
@HostListener('click', ['$event'])
- 我需要监听父容器上的 'click' 事件以确定它何时被选中。所以我不能简单地摆脱它。
当您将鼠标悬停在内部子组件上时,您可以单击以拖动元素的边框并调整其大小。我正在努力解决的是父组件中 'click' 处理程序的事件传播。
我的问题是 @HostListener
click 事件是从父组件触发的,而我并没有预料到它是这样。即,如果您拖动子组件的边框。鼠标松开时,ParentComponent
的点击事件被触发。
我的理解是
- 为防止 click 事件的事件传播,应该在 mousedown 事件中捕获它。因此
stopPropagation()
但父组件仍在拾取点击事件。
有很多关于使用 return false
的评论。但是,根据其他问题,这似乎不是原因,如此处所示。
- StopPropagation vs prevent Default
- When to use PreventDefault( ) vs Return false? [duplicate]
也就是说,即使我在指令中添加 return false
,事件仍然会冒泡。
感谢任何关于我可能遗漏的见解。
澄清一下,事件侦听器将按以下顺序触发,从事件发起的元素开始
directive -> child -> parent -> document
据我所知,您遇到的问题归结为多种因素:
当你拖动边框时,鼠标最终会在child元素之外,所以你最终会触发parent.mouseup -> document.mouseup
。要在指令中捕获事件,您必须收听 document.mouseup
.
您的指令正在侦听 document.mouseup
。 parent.mouseup
在 document.mouseup
之前触发,因此您无法在指令中停止传播。
让我们看看当您向下拖动边框时会发生什么:
你按下边框。这会触发 directive.mousedown -> child.mousedown -> ...
。你在指令中停止传播,所以一切都很好。
您将鼠标拖到底部,光标现在位于child元素之外。
你松开鼠标。请注意,我们不再位于 child 元素中,因此这会触发 parent.mouseup -> document.mouseup
。 Parent 处理程序将 运行 在文档处理程序之前,因此您无法停止它。
由于 down
和 up
发生在 parent 控制器中,现在触发点击。同样,我们没有 child 元素,所以触发器是 parent.click -> document.click
。您无法真正阻止这种情况的发生,因为 parent 是第一个处理程序。
我会提出以下解决方案,尽管它看起来有点老套:
- 我们可以自己滚动,而不是使用
HostListener('click')
触发 parent 单击:我们将收听 parent.mousedown
和 parent.mouseup
。如果它们快速连续发生,则假设是点击,因此我们 "select" parent.
// parent.component.ts
export class ParentComponent {
parentClicked: boolean = false;
down?: number;
manualClick(): void {
this.parentClicked = true;
console.log(`parent manual click`);
}
@HostListener('mousedown', ['$event'])
onMouseDown( event: MouseEvent): void {
console.log('parent down', event.target);
this.down = Date.now();
}
@HostListener('mouseup', ['$event'])
onMouseUp( event: MouseEvent): void {
console.log('parent up');
// Assume that if it's released within 100 ms, it's a click.
if (this.down && Date.now() - this.down < 100) {
this.manualClick();
this.down = undefined;
}
}
}
由于我们不监听点击事件,现在可以了:
- 你在指令中按下边界并停止传播,所以
parent.mousedown
永远不会发生。
- 您将鼠标拖到 child 之外并进入 parent 元素。
- 您松开鼠标,这会触发
parent.mouseup -> document.mouseup
。由于 parent.mousedown
未被触发,down = undefined
,因此我们的 "click logic" 不将此计为一次点击。
- 事件冒泡到
document.mouseup
,您最终在指令中处理了它。
现在,这里有一个潜在的小问题:当您点击 child 元素时,您将触发我们的 parent 的点击逻辑,因为
child.mousedown-> parent.mousedown
后跟 child.mouseup -> parent.mouseup
。如果您不希望这种情况发生,您可以在 child:
中停止 mousedown
事件的传播
// child.component.ts
@HostListener('mousedown', ['$event'])
onMouseDown( event: MouseEvent): void {
console.log('child down');
event.stopPropagation();
event.preventDefault();
}
我想做什么:
- 创建一个 'resize' angular 指令,允许用户拖动 DOM 元素的边框并调用自定义应用程序逻辑(完成)
- 确保点击不会冒泡到父组件。
stackblitz link -> stackblitz example
组件层次结构是这样的
- app.component>parent.component>child.component(子组件使用'resize'指令)
在'resize.directive'。
- mousedown事件被捕获,
event.stopPropagation()
&event.preventDefault()
用于防止事件冒泡 - mousedown 事件只有当用户的鼠标在边界上时才会被捕获
child.component
- 是一个简单的 div 使用 'resize' 指令
父组件
- 使用
@HostListener('click', ['$event'])
- 我需要监听父容器上的 'click' 事件以确定它何时被选中。所以我不能简单地摆脱它。
当您将鼠标悬停在内部子组件上时,您可以单击以拖动元素的边框并调整其大小。我正在努力解决的是父组件中 'click' 处理程序的事件传播。
我的问题是 @HostListener
click 事件是从父组件触发的,而我并没有预料到它是这样。即,如果您拖动子组件的边框。鼠标松开时,ParentComponent
的点击事件被触发。
我的理解是
- 为防止 click 事件的事件传播,应该在 mousedown 事件中捕获它。因此
stopPropagation()
但父组件仍在拾取点击事件。
有很多关于使用 return false
的评论。但是,根据其他问题,这似乎不是原因,如此处所示。
- StopPropagation vs prevent Default
- When to use PreventDefault( ) vs Return false? [duplicate]
也就是说,即使我在指令中添加 return false
,事件仍然会冒泡。
感谢任何关于我可能遗漏的见解。
澄清一下,事件侦听器将按以下顺序触发,从事件发起的元素开始
directive -> child -> parent -> document
据我所知,您遇到的问题归结为多种因素:
当你拖动边框时,鼠标最终会在child元素之外,所以你最终会触发
parent.mouseup -> document.mouseup
。要在指令中捕获事件,您必须收听document.mouseup
.您的指令正在侦听
document.mouseup
。parent.mouseup
在document.mouseup
之前触发,因此您无法在指令中停止传播。
让我们看看当您向下拖动边框时会发生什么:
你按下边框。这会触发
directive.mousedown -> child.mousedown -> ...
。你在指令中停止传播,所以一切都很好。您将鼠标拖到底部,光标现在位于child元素之外。
你松开鼠标。请注意,我们不再位于 child 元素中,因此这会触发
parent.mouseup -> document.mouseup
。 Parent 处理程序将 运行 在文档处理程序之前,因此您无法停止它。由于
down
和up
发生在 parent 控制器中,现在触发点击。同样,我们没有 child 元素,所以触发器是parent.click -> document.click
。您无法真正阻止这种情况的发生,因为 parent 是第一个处理程序。
我会提出以下解决方案,尽管它看起来有点老套:
- 我们可以自己滚动,而不是使用
HostListener('click')
触发 parent 单击:我们将收听parent.mousedown
和parent.mouseup
。如果它们快速连续发生,则假设是点击,因此我们 "select" parent.
// parent.component.ts
export class ParentComponent {
parentClicked: boolean = false;
down?: number;
manualClick(): void {
this.parentClicked = true;
console.log(`parent manual click`);
}
@HostListener('mousedown', ['$event'])
onMouseDown( event: MouseEvent): void {
console.log('parent down', event.target);
this.down = Date.now();
}
@HostListener('mouseup', ['$event'])
onMouseUp( event: MouseEvent): void {
console.log('parent up');
// Assume that if it's released within 100 ms, it's a click.
if (this.down && Date.now() - this.down < 100) {
this.manualClick();
this.down = undefined;
}
}
}
由于我们不监听点击事件,现在可以了:
- 你在指令中按下边界并停止传播,所以
parent.mousedown
永远不会发生。 - 您将鼠标拖到 child 之外并进入 parent 元素。
- 您松开鼠标,这会触发
parent.mouseup -> document.mouseup
。由于parent.mousedown
未被触发,down = undefined
,因此我们的 "click logic" 不将此计为一次点击。 - 事件冒泡到
document.mouseup
,您最终在指令中处理了它。
现在,这里有一个潜在的小问题:当您点击 child 元素时,您将触发我们的 parent 的点击逻辑,因为
child.mousedown-> parent.mousedown
后跟 child.mouseup -> parent.mouseup
。如果您不希望这种情况发生,您可以在 child:
mousedown
事件的传播
// child.component.ts
@HostListener('mousedown', ['$event'])
onMouseDown( event: MouseEvent): void {
console.log('child down');
event.stopPropagation();
event.preventDefault();
}