Wie erkennt man Klicks außerhalb eines Elements in Angular?
Lesezeit: 9 Minuten
Lulutho Mgwali
Dies div wird dynamisch auf der Seite als Ostpanel angezeigt, wenn die open panel Schaltfläche angeklickt wird. Der bool showEastPanel Variable wird verwendet, um das Ostfenster zu öffnen und zu schließen. Ich versuche zu verwenden (clickoutside) um das Panel zu schließen (Einstellung showEastPanel auf false) aber das geöffnete Panel läuft zuerst auf dem Angular Hook und das Panel wird auf true und dann auf false gesetzt und das Panel wird nicht angezeigt. Ist der Weg zum Umfang clickoutside um die Schaltfläche nicht einzufügen?
Ich würde dies tun, indem ich den von Angular empfohlenen Ansatz verwende, der auch einfach Apps in Umgebungen ohne DOM-Zugriff entwickelt, meine ich Renderer 2 -Klasse, die eine von Angular bereitgestellte Abstraktion in Form eines Dienstes ist, der es ermöglicht, Elemente Ihrer App zu manipulieren, ohne das DOM direkt berühren zu müssen.
Bei diesem Ansatz müssen Sie injizieren Renderer2 in Ihren Komponentenkonstruktor, der die Renderer2 lass uns zu listen Ereignisse elegant auszulösen. Es braucht nur das Element, das Sie hören werden, als erstes Argument, das sein kann window, document, body oder eine andere Elementreferenz. Für das zweite Argument benötigen wir das Ereignis, auf das wir hören werden, was in diesem Fall ist click, und das dritte Argument ist eigentlich die Callback-Funktion, die wir mit der Pfeilfunktion ausführen.
this.renderer.listen('window', 'click',(e:Event)=>{ // your code here})
Der Rest der Lösung ist einfach, Sie müssen nur ein boolesches Flag setzen, das den Status der Sichtbarkeit des Menüs (oder des Bedienfelds) beibehält, und wir sollten es zuweisen false zu diesem Flag, wenn es außerhalb des Menüs angeklickt wird.
HTML
<button #toggleButton (click)="toggleMenu()"> Toggle Menu</button>
<div class="menu" *ngIf="isMenuOpen" #menu>
I'm the menu. Click outside to close me
</div>
app.komponente.ts
export class AppComponent {
/**
* This is the toogle button elemenbt, look at HTML and see its defination
*/
@ViewChild('toggleButton') toggleButton: ElementRef;
@ViewChild('menu') menu: ElementRef;
constructor(private renderer: Renderer2) {
/**
* This events get called by all clicks on the page
*/
this.renderer.listen('window', 'click',(e:Event)=>{
/**
* Only run when toggleButton is not clicked
* If we don't check this, all clicks (even on the toggle button) gets into this
* section which in the result we might never see the menu open!
* And the menu itself is checked here, and it's where we check just outside of
* the menu and button the condition abbove must close the menu
*/
if(e.target !== this.toggleButton.nativeElement && e.target!==this.menu.nativeElement){
this.isMenuOpen=false;
}
});
}
isMenuOpen = false;
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
}
Auch hier, wenn Sie die funktionierende Demo sehen möchten, verwenden Sie diesen Link: Stackblitz-Demo
Zu erkennen, wo genau das Klicken war, kann wegen des Blubberns sehr mühsam sein. Mach lieber den Test mit path Array – hat sie alle! 😉 if (evt.path.indexOf( this.toggleButton.nativeElement ) === -1 ) {...}
– Pedro Ferreira
24. März ’19 bei 0:39
Würde es Ihnen etwas ausmachen, darauf hinzuweisen, wo ich diese Empfehlung in Angular-Dokumenten finden kann?
– MMalke
10. August ’20 um 13:50
Perfekt gearbeitet! Und ich denke, es ist in vielen Dingen nützlich.
– nipun-kavishka
16. August ’20 um 10:31
Wenn Sie auch ein Klickereignis bei Zielkindern erkennen müssen, verwenden Sie Folgendes: !this.toggleButton.nativeElement.contains(e.target) wie auf stackoverflow.com/a/57391798/9348886 erklärt
– Mosrainis
8. Dezember ’20 um 13:56
Wenn Sie das Schließen des Menüs beim Klicken auf die inneren Elemente des Menüs verhindern möchten, e.target !== this.toggleButton.nativeElement && e.target !== this.menu.nativeElement && !this.menu.nativeElement.contains(e.target)
– JotK.
7. Mai ’21 um 9:52
du kannst sowas machen
@HostListener('document:mousedown', ['$event'])
onGlobalClick(event): void {
if (!this.elementRef.nativeElement.contains(event.target)) {
// clicked outside => close dropdown list
this.isOpen = false;
}
}
und verwende *ngIf=isOpen für das Panel
Was ist if (!this.elementRef.nativeElement.contains(event.target))?
– Ben Racicot
19. Februar ’19 um 13:12
this – import { Component, ElementRef, HostListener, Input } from ‘@angular/core’;
– Energie
10. Juli ’19 um 4:01
Obwohl der HostListener teuer in der Leistung ist, hat dieser für mich funktioniert. Anstelle von @ViewChild habe ich ein const innerhalb der Methode verwendet: const clickedElement = event.target as HTMLElement;if (clickedElement.classList[0] !== 'my-element') {...
– Diego Ortiz
14. Januar ’20 um 21:27
kyselm
Ich möchte die Lösung hinzufügen, die mir geholfen hat, das richtige Ergebnis zu erzielen.
Wenn Sie eingebettete Elemente verwenden und einen Klick auf das übergeordnete Element erkennen möchten, verweist event.target auf das grundlegende untergeordnete Element.
HTML
<div #toggleButton (click)="toggleMenu()">
<u>Toggle Menu</u>
<span class="some-icon"></span>
</div>
<div #menu class="menu" *ngIf="isMenuOpen">
<h1>I'm the menu.</h1>
<div>
I have some complex content containing multiple children.
<i>Click outside to close me</i>
</div>
</div>
ich klicke auf “Menü umschalten” text, event.target gibt Verweis auf . zurück ‘du’ Element statt #Umschaltknopf div.
Für diesen Fall habe ich die Lösung von M98 einschließlich Renderer2 verwendet, aber die Bedingung in die von Sujays Antwort geändert.
ToggleButton.nativeElement.contains(e.target) gibt zurück wahr selbst wenn das Ziel des Click-Ereignisses in den untergeordneten Elementen von nativeElement liegt, was das Problem löst.
Komponente.ts
export class AppComponent {
/**
* This is the toogle button element, look at HTML and see its definition
*/
@ViewChild('toggleButton') toggleButton: ElementRef;
@ViewChild('menu') menu: ElementRef;
isMenuOpen = false;
constructor(private renderer: Renderer2) {
/**
* This events get called by all clicks on the page
*/
this.renderer.listen('window', 'click',(e:Event)=>{
/**
* Only run when toggleButton is not clicked
* If we don't check this, all clicks (even on the toggle button) gets into this
* section which in the result we might never see the menu open!
* And the menu itself is checked here, and it's where we check just outside of
* the menu and button the condition abbove must close the menu
*/
if(!this.toggleButton.nativeElement.contains(e.target) && !this.menu.nativeElement.contains(e.target)) {
this.isMenuOpen=false;
}
});
}
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
}
Hier ist eine wiederverwendbare Anweisung, die auch den Fall abdeckt, wenn sich das Element in einem ngIf befindet:
Was mir gefehlt hat, war setTimeout(…, 0), das sicherstellt, dass die Überprüfung geplant wird, nachdem die Komponente, die die Direktive verwendet, gerendert wurde.
Ich habe eine allgemeine Funktion erstellt, um das Menü bei einem Klick von außen zu schließen und das Schließen zu verhindern, wenn ein Klick auf bestimmte Elemente ausgelöst wird.
constructor(private renderer: Renderer2) {
/**
* this.renderer instance would be shared with the other multiple same components
* so you should have one more flag to divide the components
* the only dropdown with mouseInFilter which is false should be close
*/
this.renderer.listen('document', 'click', (e: Event) => {
if (!this.mouseInFilter) {
// this is the time to hide dropdownVisible
this.dropdownVisible = false;
}
});
}
onMouseOutFilter(e) {
this.mouseInFilter = false;
}
onMouseEnterFilter(e) {
this.mouseInFilter = true;
}
und stellen Sie sicher, dass der defaultValue von mouseInFilter false ist;
Hast du versucht zu bestehen?
$event
zuClosePanel
und Ruf anpreventDefault
oderstopPropagation
InnerhalbClosePanel
?– ADreNaLiNe-DJ
3. Juli ’18 um 9:00