如何在处理嵌套组件时停止触发 'Click' 事件

How to stop 'Click' event from being triggered when dealing with nested components

我想做什么:

stackblitz link -> stackblitz example

组件层次结构是这样的

在'resize.directive'。

child.component

父组件

当您将鼠标悬停在内部子组件上时,您可以单击以拖动元素的边框并调整其大小。我正在努力解决的是父组件中 'click' 处理程序的事件传播。

我的问题是 @HostListener click 事件是从父组件触发的,而我并没有预料到它是这样。即,如果您拖动子组件的边框。鼠标松开时,ParentComponent 的点击事件被触发。

我的理解是

但父组件仍在拾取点击事件。

有很多关于使用 return false 的评论。但是,根据其他问题,这似乎不是原因,如此处所示。

也就是说,即使我在指令中添加 return false,事件仍然会冒泡。

感谢任何关于我可能遗漏的见解。

澄清一下,事件侦听器将按以下顺序触发,从事件发起的元素开始

directive -> child -> parent -> document

据我所知,您遇到的问题归结为多种因素:

  1. 当你拖动边框时,鼠标最终会在child元素之外,所以你最终会触发parent.mouseup -> document.mouseup。要在指令中捕获事件,您必须收听 document.mouseup.

  2. 您的指令正在侦听 document.mouseupparent.mouseupdocument.mouseup 之前触发,因此您无法在指令中停止传播。

让我们看看当您向下拖动边框时会发生什么:

  1. 你按下边框。这会触发 directive.mousedown -> child.mousedown -> ...。你在指令中停止传播,所以一切都很好。

  2. 您将鼠标拖到底部,光标现在位于child元素之外。

  3. 你松开鼠标。请注意,我们不再位于 child 元素中,因此这会触发 parent.mouseup -> document.mouseup。 Parent 处理程序将 运行 在文档处理程序之前,因此您无法停止它。

  4. 由于 downup 发生在 parent 控制器中,现在触发点击。同样,我们没有 child 元素,所以触发器是 parent.click -> document.click。您无法真正阻止这种情况的发生,因为 parent 是第一个处理程序。

我会提出以下解决方案,尽管它看起来有点老套:

  1. 我们可以自己滚动,而不是使用 HostListener('click') 触发 parent 单击:我们将收听 parent.mousedownparent.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;
    }
  }
}

由于我们不监听点击事件,现在可以了:

  1. 你在指令中按下边界并停止传播,所以parent.mousedown永远不会发生。
  2. 您将鼠标拖到 child 之外并进入 parent 元素。
  3. 您松开鼠标,这会触发 parent.mouseup -> document.mouseup。由于 parent.mousedown 未被触发,down = undefined,因此我们的 "click logic" 不将此计为一次点击。
  4. 事件冒泡到 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();
  }