英文:
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('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.
答案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 '@angular/core'; // and other modules from /core
(...)
constructor(private elem: ElementRef) {}
@HostListener('document:click', ['$event'])
docClicked(event: Event) {
if (this.elem.nativeElement.contains(event.target))
console.log('clicked inside');
else {
console.log('clicked outside');
}
}
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 '@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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论