Câu trả lời:
import { Component, ElementRef, HostListener, Input } from '@angular/core';
@Component({
selector: 'selector',
template: `
<div>
{{text}}
</div>
`
})
export class AnotherComponent {
public text: String;
@HostListener('document:click', ['$event'])
clickout(event) {
if(this.eRef.nativeElement.contains(event.target)) {
this.text = "clicked inside";
} else {
this.text = "clicked outside";
}
}
constructor(private eRef: ElementRef) {
this.text = 'no clicks yet';
}
}
Một thay thế cho câu trả lời của AMagyar. Phiên bản này hoạt động khi bạn nhấp vào phần tử bị xóa khỏi DOM bằng ngIf.
http://plnkr.co/edit/4mrn4GjM95uvSbQtxrAS?p=preview
private wasInside = false;
@HostListener('click')
clickInside() {
this.text = "clicked inside";
this.wasInside = true;
}
@HostListener('document:click')
clickout() {
if (!this.wasInside) {
this.text = "clicked outside";
}
this.wasInside = false;
}
Việc liên kết với tài liệu khi nhấp qua @Hostlistener rất tốn kém. Nó có thể và sẽ có tác động rõ ràng về hiệu suất nếu bạn sử dụng quá mức (ví dụ: khi tạo thành phần tùy chỉnh thả xuống và bạn có nhiều phiên bản được tạo trong một biểu mẫu).
Tôi khuyên bạn nên thêm @Hostlistener () vào sự kiện nhấp vào tài liệu chỉ một lần bên trong thành phần ứng dụng chính của bạn. Sự kiện sẽ đẩy giá trị của phần tử mục tiêu được nhấp vào bên trong một chủ đề công khai được lưu trữ trong một dịch vụ tiện ích toàn cầu.
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent {
constructor(private utilitiesService: UtilitiesService) {}
@HostListener('document:click', ['$event'])
documentClick(event: any): void {
this.utilitiesService.documentClickedTarget.next(event.target)
}
}
@Injectable({ providedIn: 'root' })
export class UtilitiesService {
documentClickedTarget: Subject<HTMLElement> = new Subject<HTMLElement>()
}
Bất cứ ai quan tâm đến phần tử mục tiêu được nhấp nên đăng ký chủ đề công khai của dịch vụ tiện ích của chúng tôi và hủy đăng ký khi thành phần bị phá hủy.
export class AnotherComponent implements OnInit {
@ViewChild('somePopup', { read: ElementRef, static: false }) somePopup: ElementRef
constructor(private utilitiesService: UtilitiesService) { }
ngOnInit() {
this.utilitiesService.documentClickedTarget
.subscribe(target => this.documentClickListener(target))
}
documentClickListener(target: any): void {
if (this.somePopup.nativeElement.contains(target))
// Clicked inside
else
// Clicked outside
}
Các câu trả lời được đề cập ở trên là đúng nhưng điều gì sẽ xảy ra nếu bạn đang thực hiện một quá trình nặng sau khi mất tập trung từ thành phần liên quan. Đối với điều đó, tôi đã đưa ra một giải pháp có hai cờ mà quá trình sự kiện tiêu điểm sẽ chỉ diễn ra khi chỉ mất tiêu điểm từ thành phần có liên quan.
isFocusInsideComponent = false;
isComponentClicked = false;
@HostListener('click')
clickInside() {
this.isFocusInsideComponent = true;
this.isComponentClicked = true;
}
@HostListener('document:click')
clickout() {
if (!this.isFocusInsideComponent && this.isComponentClicked) {
// do the heavy process
this.isComponentClicked = false;
}
this.isFocusInsideComponent = false;
}
Hy vọng điều này sẽ giúp bạn. Sửa tôi Nếu có bỏ lỡ bất cứ điều gì.
Bạn có thể sử dụng phương thức useclickOutside () từ gói https://www.npmjs.com/package/ng-click-outside
Câu trả lời của ginalx nên được đặt làm imo mặc định: phương pháp này cho phép nhiều tối ưu hóa.
Vấn đề
Giả sử rằng chúng tôi có một danh sách các mục và trên mỗi mục, chúng tôi muốn bao gồm một menu cần được chuyển đổi. Chúng tôi bao gồm một nút bật / tắt tự lắng nghe một click
sự kiện (click)="toggle()"
, nhưng chúng tôi cũng muốn chuyển đổi menu bất cứ khi nào người dùng nhấp vào bên ngoài nó. Nếu danh sách các mục tăng lên và chúng tôi đính kèm một @HostListener('document:click')
trên mọi menu, thì mọi menu được tải trong mục sẽ bắt đầu lắng nghe khi nhấp chuột trên toàn bộ tài liệu, ngay cả khi menu được tắt. Bên cạnh các vấn đề về hiệu suất rõ ràng, điều này là không cần thiết.
Ví dụ: bạn có thể đăng ký bất cứ khi nào cửa sổ bật lên được bật lên qua một lần nhấp và chỉ bắt đầu nghe "nhấp chuột bên ngoài" sau đó.
isActive: boolean = false;
// to prevent memory leaks and improve efficiency, the menu
// gets loaded only when the toggle gets clicked
private _toggleMenuSubject$: BehaviorSubject<boolean>;
private _toggleMenu$: Observable<boolean>;
private _toggleMenuSub: Subscription;
private _clickSub: Subscription = null;
constructor(
...
private _utilitiesService: UtilitiesService,
private _elementRef: ElementRef,
){
...
this._toggleMenuSubject$ = new BehaviorSubject(false);
this._toggleMenu$ = this._toggleMenuSubject$.asObservable();
}
ngOnInit() {
this._toggleMenuSub = this._toggleMenu$.pipe(
tap(isActive => {
logger.debug('Label Menu is active', isActive)
this.isActive = isActive;
// subscribe to the click event only if the menu is Active
// otherwise unsubscribe and save memory
if(isActive === true){
this._clickSub = this._utilitiesService.documentClickedTarget
.subscribe(target => this._documentClickListener(target));
}else if(isActive === false && this._clickSub !== null){
this._clickSub.unsubscribe();
}
}),
// other observable logic
...
).subscribe();
}
toggle() {
this._toggleMenuSubject$.next(!this.isActive);
}
private _documentClickListener(targetElement: HTMLElement): void {
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
if (!clickedInside) {
this._toggleMenuSubject$.next(false);
}
}
ngOnDestroy(){
this._toggleMenuSub.unsubscribe();
}
Và, trong *.component.html
:
<button (click)="toggle()">Toggle the menu</button>
tap
toán tử. Thay vào đó, hãy sử dụng skipWhile(() => !this.isActive), switchMap(() => this._utilitiesService.documentClickedTarget), filter((target) => !this._elementRef.nativeElement.contains(target)), tap(() => this._toggleMenuSubject$.next(false))
. Bằng cách này, bạn sử dụng nhiều RxJ hơn và bỏ qua một số đăng ký.
Cải thiện @J. Frankenstein câu trả lời
@HostListener('click')
clickInside($event) {
this.text = "clicked inside";
$event.stopPropagation();
}
@HostListener('document:click')
clickout() {
this.text = "clicked outside";
}
bạn có thể gọi hàm sự kiện như (tiêu điểm) hoặc (làm mờ) rồi bạn đặt mã của bạn
<div tabindex=0 (blur)="outsideClick()">raw data </div>
outsideClick() {
alert('put your condition here');
}