Wie erkennt man Klicks außerhalb eines Elements in Angular?

Lesezeit: 9 Minuten

Wie erkennt man Klicks auserhalb eines Elements in Angular
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?

<div [ngClass]="{'d-none': !showEastPanel, 'east-panel-container': showEastPanel}" (clickOutside)="ClosePanel()">
      <div id="east-panel">
        <ng-template #eastPanel></ng-template>
      </div>
</div>

<button (click)="ShowPanel()">Open Panel</button>

  • Hast du versucht zu bestehen? $event zu ClosePanel und Ruf an preventDefault oder stopPropagation Innerhalb ClosePanel ?

    – ADreNaLiNe-DJ

    3. Juli ’18 um 9:00

Wie erkennt man Klicks auserhalb eines Elements in Angular
Mohammad Kermani

Hier der Link zur funktionierenden Demo: Stackblitz-Demo

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


1641733769 658 Wie erkennt man Klicks auserhalb eines Elements in Angular
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:

import { Directive, ElementRef, Optional, Inject, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { fromEvent, Subscription } from 'rxjs';
import { DOCUMENT } from '@angular/common';
import { filter } from 'rxjs/operators';

@Directive({
  selector: '[outsideClick]',
})
export class OutsideClickDirective implements OnInit, OnDestroy {
  @Output('outsideClick') outsideClick = new EventEmitter<MouseEvent>();

  private subscription: Subscription;

  constructor(private element: ElementRef, @Optional() @Inject(DOCUMENT) private document: any) {}

  ngOnInit() {
    setTimeout(() => {
      this.subscription = fromEvent<MouseEvent>(this.document, 'click')
        .pipe(
          filter(event => {
            const clickTarget = event.target as HTMLElement;
            return !this.isOrContainsClickTarget(this.element.nativeElement, clickTarget);
          }),
        )
        .subscribe(event => this.outsideClick.emit());
    }, 0);
  }

  private isOrContainsClickTarget(element: HTMLElement, clickTarget: HTMLElement) {
    return element == clickTarget || element.contains(clickTarget);
  }

  ngOnDestroy() {
    if (this.subscription) this.subscription.unsubscribe();
  }
}

Anerkennung an https://github.com/ngez/platform, ich habe die meiste Logik daraus.

Was mir gefehlt hat, war setTimeout(…, 0), das sicherstellt, dass die Überprüfung geplant wird, nachdem die Komponente, die die Direktive verwendet, gerendert wurde.

Nützliche Links:

Ich mag die Antwort von Sujay. Wenn Sie stattdessen eine Direktive erstellen möchten (zur Verwendung in mehreren Komponenten). So würde ich es machen.

import {
  Directive,
  EventEmitter,
  HostListener,
  Output,
  ElementRef,
} from '@angular/core';

@Directive({
  selector: '[outsideClick]',
})
export class OutsideClickDirective {
  @Output()
  outsideClick: EventEmitter<MouseEvent> = new EventEmitter();

  @HostListener('document:mousedown', ['$event'])
  onClick(event: MouseEvent): void {
    if (!this.elementRef.nativeElement.contains(event.target)) {
      this.outsideClick.emit(event);
    }
  }

  constructor(private elementRef: ElementRef) {}
}

Sie würden dann die Direktive wie folgt verwenden:

<div class="menu" *ngIf="isMenuOpen" (outsideClick)="isMenuOpen = false" outsideClick #menu>
  I'm the menu. Click outside to close me
</div>

1641733769 514 Wie erkennt man Klicks auserhalb eines Elements in Angular
Code-Info

Vereinfachter Code mit Demo auf: StackBlitz

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.

HTML

<button (click)="toggleMenu(); preventCloseOnClick()">Toggle Menu</button>
<ul (click)="preventCloseOnClick()" *ngIf="menuOpen">
  <li>Menu 1</li>
  <li>Menu 2</li>
  <li>Menu 3</li>
  <li>Menu 4</li>
  <li>Menu 5</li>
</ul>

TS

import { Component, VERSION, Renderer2 } from '@angular/core';

export class AppComponent {
  menuOpen: boolean = false;
  menuBtnClick: boolean = false;

  constructor(private renderer: Renderer2) {
    this.renderer.listen('window', 'click', (e: Event) => {
      if (!this.menuBtnClick) {
        this.menuOpen = false;
      }
      this.menuBtnClick = false;
    });
  }
  toggleMenu() {
    this.menuOpen = !this.menuOpen;
  }
  preventCloseOnClick() {
    this.menuBtnClick = true;
  }
}

1641733769 651 Wie erkennt man Klicks auserhalb eines Elements in Angular
YOSEPH NOH

Im Gegensatz zu den vorherigen Antworten habe ich es anders gemacht.

ich setze mouseleave, mouseenter Ereignis im Dropdown-Menü

<div
    class="dropdown-filter"
    (mouseleave)="onMouseOutFilter($event)"
    (mouseenter)="onMouseEnterFilter($event)"
  >
    <ng-container *ngIf="dropdownVisible">
      <input
        type="text"
        placeholder="search.."
        class="form-control"
        [(ngModel)]="keyword"
        id="myInput"
        (keyup)="onKeyUp($event)"
      />
    </ng-container>
    <ul
      class="dropdown-content"
      *ngIf="dropdownVisible"
    >
      <ng-container *ngFor="let item of filteredItems; let i = index">
        <li
          (click)="onClickItem($event, item)"
          [ngStyle]="listWidth && {width: listWidth + 'px'}"
        >
          <span>{{ item.label }}</span>
        </li>
      </ng-container>
    </ul>
  </div>
  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;

  ngOnInit() {
    this.mouseInFilter = false;
    this.dropdownVisible = false;
  }

und wenn das Dropdown-Menü sichtbar sein sollte, wird mouseInFilter wahr sein

  toggleDropDownVisible() {
    if (!this.dropdownVisible) {
      this.mouseInFilter = true;
    }
    this.dropdownVisible = !this.dropdownVisible;
  }

.

212440cookie-checkWie erkennt man Klicks außerhalb eines Elements in Angular?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy