首页 > 解决方案 > 如何检查事件目标是否在一个元素内?

问题描述

我有“卡片”组件,它正在侦听外部单击或滚动事件,并在发生此事件时被销毁。

@HostListener('document:mousedown', ['$event'])
public onEvent(e: Event): void {
    let el = this.elementRef.nativeElement;
    if (e.target !== el && !el.contains(e.target)) {
        this.destroyCard();
    }
}

ngAfterViewInit() {
  document.addEventListener('scroll', this.onEvent, true);
}

当我从这个组件创建模态窗口时,问题就出现了,它是在应用程序视图根目录中的这个组件之外创建的。当我单击模态窗口或在其上滚动时,我的卡片组件与模态窗口一起被销毁。

这是带有 click的沙盒堆栈闪电战。当您单击模态窗口时,它应该保留。

更新。用 event.stopPropagation() 修复。

但它不适用于通常不像滚动这样的气泡的事件。请参阅带有滚动的 stackblitz

我只看到一个解决方案:创建将侦听事件的服务,我的 Card 组件和 Modal 窗口组件将订阅并将它们的节点添加到数组中,该数组将检查事件目标是否放置在该节点内。

也许有人知道更好的解决方案?

标签: angulardom-events

解决方案


您可以mousedown在模态组件中处理事件,并调用event.stopPropagation()以确保它没有达到document级别。你可以在这个 stackblitz中看到结果。

export class ModalWindowComponent {
    @HostListener('mousedown', ['$event'])
    public processMouseDown(event: MouseEvent): void {
        event.stopPropagation();
    }
}

重要提示:一定要打电话event.stopPropagation(),不要event.stopImmediatePropagation()

  • event.stopPropagation()防止事件冒泡到元素的祖先(参见MDN 文档
  • event.stopImmediatePropagation()只是防止调用元素上的其余侦听器(请参阅MDN 文档

对于滚动事件,调用event.stopPropagation不起作用。如果滚动元素具有特定属性,例如data-keepmodalopen="true"(小写),您可以保持模式打开。这是这种检查的代码(参见这个 stackblitz的演示):

<div class="container">Modal window. I should stay
  <div class="scroll-container" data-keepmodalopen="true">
    ...
  </div>
</div>
public onScroll(e: MouseEvent): void {
  if (this.window && !this.keepModelOpen(e.target as HTMLElement)) {
    this.window.destroy();
    this.window = null;
  }
}

public keepModelOpen(element: HTMLElement): boolean {
  return element.dataset.keepmodalopen === "true";
}

推荐阅读