当处理嵌套组件时,如何阻止 "Click "事件被触发?

问题描述 投票:1回答:1

我想做的是

  • 创建一个 "调整大小 "的angular指令,允许用户拖动DOM元素的边框,并调用自定义应用逻辑(完成)。
  • 确保点击不会冒泡到父组件。

stackblitz链接 -> stackblitz示例

组件的层次结构是这样的

  • app.component > parent.component > child.component (子组件使用'resize'指令)

中的'resize.directive'。

  • 捕捉到mousedown事件并 event.stopPropagation() &amp.用于防止事件冒泡。event.preventDefault() 是用来防止事件冒泡的
  • 只有当用户的鼠标在边界上时,mousedown事件才会被捕获。

孩子的组件

  • 是一个简单的div,使用'resize'指令。

父组件

  • 用途 @HostListener('click', ['$event'])
  • 我需要监听父容器上的'click'事件,以确定它何时被选中,所以我不能简单地摆脱它。

当你把鼠标放在内部的子组件上时,你可以点击来拖动和调整元素的边框大小。

我的问题是 @HostListener 点击事件是从父组件中发出的,而我并不希望它是这样的。例如,如果你拖动子组件的边框,当鼠标向上时,父组件的点击事件就会发生。ParentComponent 被解雇。

我的理解是

  • 为了防止点击事件的传播,应该在mousedown事件中捕获。stopPropagation()

但父组件仍在接收点击事件。

有很多关于使用 return false......然而,根据其他问题,这似乎不是原因,如这里所示。

话说回来 return false 的指令,但事件仍在冒泡。

如果有任何关于我可能遗漏了什么的见解,我将不胜感激。

javascript angular dom-events stoppropagation
1个回答
0
投票

澄清一下,事件监听器将按照以下顺序被触发,从事件发生的元素开始。

directive -> child -> parent -> document

在我看来,你所遇到的问题归结为多种因素的结合。

  1. 当你拖动边框的时候 鼠标会停留在子元素的外面 所以你会最终触发了 parent.mouseup -> document.mouseup. 要在指令中捕获事件,你必须监听到 document.mouseup.

  2. 你的指令在听 document.mouseup. 该 parent.mouseup 前被触发。document.mouseup 所以你无法阻止指令中的传播。

让我们看看当你把边界拖下来会发生什么。

  1. 你按下边界 这将触发 directive.mousedown -> child.mousedown -> .... 你在指令中停止了传播,所以一切正常。

  2. 你把鼠标拖到底部,光标现在在子元素外面。

  3. 你释放鼠标。请注意,我们已经不在子元素中了,所以这将会触发 parent.mouseup -> document.mouseup. 父处理程序将在文档处理程序之前运行,所以你不能停止它。

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

我建议采用下面的解决方案,尽管它看起来有点黑。

  1. 不使用 HostListener('click')我们可以滚动自己的:我们将听取 parent.mousedownparent.mouseup. 如果它们接连发生,我们就会认为这是一个点击,所以我们 "选择 "父体。
// 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. 你按下border键,然后在指令中停止传播,所以... ... parent.mousedown 永远不会发生。
  2. 你把鼠标拖到子元素外面,然后拖到父元素中。
  3. 您释放鼠标,这将触发 parent.mouseup -> document.mouseup. 由于 parent.mousedown 没有被触发。down = undefined,所以我们的 "点击逻辑 "并不能算作点击。
  4. 该事件冒泡到 document.mouseup,最后在你的指令中处理它。

现在,这里有一个小的潜在问题:当你在子元素中点击时,你会触发我们父元素的点击逻辑,因为child.mousedown-> parent.mousedown 随后是 child.mouseup -> parent.mouseup. 如果你不希望这种情况发生,你可以停止传播的。mousedown 事件在孩子。

// child.component.ts
  @HostListener('mousedown', ['$event'])
  onMouseDown( event: MouseEvent): void {
    console.log('child down');
    event.stopPropagation();
    event.preventDefault();
  }
© www.soinside.com 2019 - 2024. All rights reserved.