监听子组件外部的点击事件?

huangapple go评论65阅读模式
英文:

How to listen for clicks outside a child component?

问题

I need to know if it is being clicked outside the CHILD COMPONENT, so I can close it.

我需要知道是否在子组件外部进行了点击,以便我可以关闭它。

I can't use mouseover any of that.

我不能使用mouseover来实现这个。

I'm using HostListener, but in the target it always sees the parent of the element and never the child.

我正在使用HostListener,但在目标中它总是看到元素的父级,而不是子级。

@HostListener('document:click', ['$event.target'])
public onClick(target) {
  console.log(target)
}

constructor(private _elementRef: ElementRef) {}

An example of what happens is the following, I have the following structure:

一个示例是,我有如下结构:

<componentA>
  <componentB />
</componentA>

I want to know if the click is being made outside of componentB, but with the code above, it is only listening to componentA.

我想知道点击是否发生在componentB的外部,但是使用上述代码,它只会监听componentA。

英文:

I need to know if it is being clicked outside the CHILD COMPONENT, so I can close it.

I can't use mosueover any of that.

I'm using Hostlistenner, but in the target it always sees the parent of the element and never the child.

  @HostListener(&#39;document:click&#39;, [&#39;$event.target&#39;])
  public onClick(target) {
    console.log(target)
  }

  constructor(private _elementRef: ElementRef) {}

An example of what happens is the following, I have the following structure:

&lt;componentA&gt;
  &lt;componentB /&gt;
&lt;/componentA&gt;

I want to know if the click is being made outside of componentB, but with the code above, it is only listening to componentA.

监听子组件外部的点击事件?

答案1

得分: 0

以下是翻译好的内容:

你需要明确检查被点击的 DOM 元素,因为监听 document 上的点击事件会始终将点击事件视为针对整个文档的点击(这是相当明显的)。所以你需要在子组件中导入 ElementRef 并使用它来检查子组件是否是点击事件的实际目标:

import { HostListener } from '@angular/core'; // 以及来自 /core 的其他模块

(...)

constructor(private elem: ElementRef) {}

@HostListener('document:click', ['$event'])
docClicked(event: Event) {
    if (this.elem.nativeElement.contains(event.target))
        console.log('点击在内部');
    else {
        console.log('点击在外部');
    }
}

然后你可以在 docClicked/else 块中执行你想要的操作。

检查 [stackblitz 示例][1] 以发射到父组件方法,该方法将设置控制在父模板中显示子组件的变量为 false

[1]: https://stackblitz.com/edit/angular-ivy-urtm6r?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fchild%2Fchild.component.ts


<details>
<summary>英文:</summary>

You need to explicitely check for the clicked DOM element, because listening to click on `document` will always treat the click as targetting the document as a whole (which is rather obvious). So you need to import `ElementRef` into child component and use it to check if the child is the actual target of the click:

     import { HostListener } from &#39;@angular/core&#39;; // and other modules from /core
     
     (...)

     constructor(private elem: ElementRef) {}

     @HostListener(&#39;document:click&#39;, [&#39;$event&#39;])
     docClicked(event: Event) {
        if (this.elem.nativeElement.contains(event.target))
           console.log(&#39;clicked inside&#39;);
        else {
           console.log(&#39;clicked outside&#39;);
        }
     }

Then you can do what you want in the docClicked/else block. 

Check [stackblitz example][1] for emitting to parent method that will set to false the var that controls the display of the child component in the parent template. 


  [1]: https://stackblitz.com/edit/angular-ivy-urtm6r?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fchild%2Fchild.component.ts

</details>



# 答案2
**得分**: 0

我注意到除了获取父组件而不是子组件之外,在生产环境中`document`会返回undefined,为了修复这两个问题,使用了以下代码:

    import { Inject, ElementRef } from '@angular/core';
    import { DOCUMENT } from '@angular/common';

    this.isOpened: boolean = false;

    constructor(
     @Inject(DOCUMENT) private _document: Document,
     private _elementRef: ElementRef
    ) {}

    ngOnInit(): void {
     this._handleOnDocumentClick();
    }

    ngOnDestroy(): void {
     this.removeEventListener('click', this._handleOnDocumentClick, true)
    }

    private _handleOnDocumentClick(): void {
     this._document.addEventListener('click', (event: Event) => {
       event.composedPath().includes(this._elementRef.nativeElement)
         ? this.isOpened = !this.isOpened
         : this.isOpened = false;
     });
    }

<details>
<summary>英文:</summary>

I noticed that in addition to being getting the parent component and not the child, in production the `document` was giving undefined, so to correct both, the following code was used:

    import { Inject, ElementRef } from &#39;@angular/core&#39;;
    import { DOCUMENT } from &#39;@angular/common&#39;;
    
    this.isOpened: boolean = false;
    
    constructor(
     @Inject(DOCUMENT) private _document: Document,
     private _elementRef: ElementRef
    ) {}
    
    ngOnInit(): void {
     this._handleOnDocumentClick();
    }
    
    ngOnDestroy(): void {
     this.removeEventListener(&#39;click&#39;, this._handleOnDocumentClick, true)
    }
    
    private _handleOnDocumentClick(): void {
     this._document.addEventListener(&#39;click&#39;, (event: Event) =&gt; {
       event.composedPath().includes(this._elementRef.nativeElement)
         ? this.isOpened = !this.isOpened
         : this.isOpened = false;
     });
    }



</details>



huangapple
  • 本文由 发表于 2023年5月22日 23:08:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76307560.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定