【问题标题】:How to stop 'Click' event from being triggered when dealing with nested components处理嵌套组件时如何停止触发“单击”事件
【发布时间】:2020-05-18 15:20:42
【问题描述】:

我正在尝试做的事情:

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

stackblitz 链接 -> stackblitz example

组件层次结构是这样的

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

在“resize.directive”中。

  • mousedown 事件被捕获,event.stopPropagation() & event.preventDefault() 用于防止事件冒泡
  • mousedown 事件仅在用户的鼠标悬停在边框上时才会被捕获

child.component

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

父组件

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

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

我的问题是 @HostListener click 事件正在从父组件触发,而我并不期望它会这样。即,如果您拖动子组件的边框。鼠标抬起时,ParentComponent 的点击事件被触发。

我的理解是这样的

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

然而父组件仍在拾取点击事件。

周围有很多 cmets 使用return false。但是,根据其他问题,这似乎不是原因,如此处所示。

话虽如此,即使我将return false 添加到指令中,事件仍然会冒泡。

感谢您对我可能遗漏的任何见解。

【问题讨论】:

    标签: javascript angular dom-events stoppropagation


    【解决方案1】:

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

    directive -> child -> parent -> document
    

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

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

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

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

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

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

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

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

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

    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. 您按下边界并停止指令中的传播,所以parent.mousedown 永远不会发生。
    2. 您将鼠标拖到子元素之外并进入父元素。
    3. 您松开鼠标,触发parent.mouseup -&gt; document.mouseup。由于parent.mousedown 没有被触发,down = undefined,所以我们的“点击逻辑”不会将其视为一次点击。
    4. 事件冒泡到document.mouseup,您最终在指令中处理它。

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

    // child.component.ts
      @HostListener('mousedown', ['$event'])
      onMouseDown( event: MouseEvent): void {
        console.log('child down');
        event.stopPropagation();
        event.preventDefault();
      }
    

    【讨论】:

    • 非常感谢,我没有意识到 mouseup 在子元素之外,因此在父元素上触发了它。非常感谢
    猜你喜欢
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    • 2015-01-02
    • 2011-12-02
    • 2010-10-18
    • 2016-04-28
    • 1970-01-01
    相关资源
    最近更新 更多