ฉันจะตรวจจับการคลิกภายนอกส่วนประกอบใน Angular ได้อย่างไร
ฉันจะตรวจจับการคลิกภายนอกส่วนประกอบใน Angular ได้อย่างไร
คำตอบ:
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';
}
}
ทางเลือกแทนคำตอบของ AMagyar เวอร์ชันนี้ใช้งานได้เมื่อคุณคลิกที่องค์ประกอบที่ถูกลบออกจาก DOM ด้วย 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;
}
การเชื่อมโยงกับเอกสารคลิกผ่าน @Hostlistener มีค่าใช้จ่ายสูง สามารถและจะมีผลกระทบต่อประสิทธิภาพที่มองเห็นได้หากคุณใช้งานมากเกินไป (ตัวอย่างเช่นเมื่อสร้างคอมโพเนนต์แบบเลื่อนลงที่กำหนดเองและคุณมีหลายอินสแตนซ์ที่สร้างในฟอร์ม)
ฉันขอแนะนำให้เพิ่ม @Hostlistener () ในเหตุการณ์การคลิกเอกสารเพียงครั้งเดียวภายในองค์ประกอบแอปหลักของคุณ เหตุการณ์ควรพุชค่าขององค์ประกอบเป้าหมายที่คลิกภายในเรื่องสาธารณะที่จัดเก็บไว้ในบริการยูทิลิตี้ส่วนกลาง
@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>()
}
ใครก็ตามที่สนใจองค์ประกอบเป้าหมายที่ถูกคลิกควรสมัครรับข่าวสารสาธารณะเกี่ยวกับบริการยูทิลิตี้ของเราและยกเลิกการสมัครเมื่อส่วนประกอบถูกทำลาย
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
}
คำตอบที่กล่าวถึงข้างต้นนั้นถูกต้อง แต่จะเกิดอะไรขึ้นหากคุณกำลังทำกระบวนการที่หนักหน่วงหลังจากสูญเสียโฟกัสจากส่วนประกอบที่เกี่ยวข้อง สำหรับสิ่งนั้นฉันมาพร้อมกับโซลูชันที่มีแฟล็กสองแบบซึ่งกระบวนการโฟกัสเอาต์จะเกิดขึ้นก็ต่อเมื่อสูญเสียโฟกัสจากส่วนประกอบที่เกี่ยวข้องเท่านั้น
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;
}
หวังว่านี่จะช่วยคุณได้ แก้ไขฉันหากพลาดอะไรไป
คำตอบของ ginalxควรถูกตั้งค่าเป็น imo เริ่มต้น: วิธีนี้ช่วยให้สามารถปรับแต่งได้หลายอย่าง
ปัญหา
สมมติว่าเรามีรายการของรายการและในทุกรายการที่เราต้องการรวมเมนูที่ต้องสลับ เรารวมการสลับบนปุ่มที่รับฟังclick
เหตุการณ์ด้วยตัวมันเอง(click)="toggle()"
แต่เราต้องการสลับเมนูเมื่อใดก็ตามที่ผู้ใช้คลิกนอก หากรายการเพิ่มขึ้นและเราแนบ@HostListener('document:click')
ทุกเมนูทุกเมนูที่โหลดภายในรายการจะเริ่มรับฟังการคลิกบนเอกสารทั้งหมดแม้ว่าเมนูจะถูกปิดอยู่ก็ตาม นอกจากปัญหาด้านประสิทธิภาพที่ชัดเจนแล้วยังไม่จำเป็น
ตัวอย่างเช่นคุณสามารถสมัครสมาชิกเมื่อใดก็ตามที่ป๊อปอัปถูกสลับผ่านการคลิกและเริ่มฟัง "คลิกนอก" ในตอนนั้น
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();
}
และใน*.component.html
:
<button (click)="toggle()">Toggle the menu</button>
tap
ดำเนินการ ให้ใช้ skipWhile(() => !this.isActive), switchMap(() => this._utilitiesService.documentClickedTarget), filter((target) => !this._elementRef.nativeElement.contains(target)), tap(() => this._toggleMenuSubject$.next(false))
ไฟล์. วิธีนี้ทำให้คุณใช้ประโยชน์จาก RxJ ได้มากขึ้นและข้ามการสมัครสมาชิกบางส่วน
คุณสามารถใช้วิธีการคลิก ClickOutside () ได้จากhttps://www.npmjs.com/package/ng-click-outside package
ปรับปรุง @J. Frankenstein answear
@HostListener('click')
clickInside($event) {
this.text = "clicked inside";
$event.stopPropagation();
}
@HostListener('document:click')
clickOutside() {
this.text = "clicked outside";
}
คุณสามารถเรียกใช้ฟังก์ชันเหตุการณ์เช่น (โฟกัส) หรือ (เบลอ) จากนั้นคุณใส่รหัสของคุณ
<div tabindex=0 (blur)="outsideClick()">raw data </div>
outsideClick() {
alert('put your condition here');
}