Dışarıdaki tıklamadaki bir açılır listeyi nasıl kapatabilirim?


144

Kullanıcı bu açılır listenin dışında herhangi bir yeri tıkladığında giriş menüsü açılır menüsümü kapatmak istiyorum ve bunu Angular2 ve Angular2 "yaklaşımı" ile yapmak istiyorum ...

Bir çözüm uyguladım, ama gerçekten kendime güvenmiyorum. Aynı sonucu elde etmenin en kolay yolunun olduğunu düşünüyorum, bu yüzden herhangi bir fikriniz varsa ... konuşalım :)!

İşte benim uygulama:

Açılır bileşen:

Bu benim açılır listemin bileşenidir:

  • Her zaman görünür ayarlı bu bileşen, (Örneğin: Bir butona kullanıcı tıklama görüntülemek için zaman) "global" rxjs konu abone usermenu içinde depolanan SubjectsService .
  • Ve her gizlendiğinde, bu konudan çıkar.
  • Tüm tıklama yerde dahilinde bu bileşen tetik şablona onClick () üstüne yöntem, sadece durdurma olay köpüren (ve uygulama bileşeni)

İşte kod

export class UserMenuComponent {

    _isVisible: boolean = false;
    _subscriptions: Subscription<any> = null;

    constructor(public subjects: SubjectsService) {
    }

    onClick(event) {
        event.stopPropagation();
    }

    set isVisible(v) {
        if( v ){
            setTimeout( () => {
this._subscriptions =  this.subjects.userMenu.subscribe((e) => {
                       this.isVisible = false;
                       })
            }, 0);
        } else {
            this._subscriptions.unsubscribe();
        }
        this._isVisible = v;
    }

    get isVisible() {
        return this._isVisible;
    }
}

Uygulama bileşeni:

Öte yandan, uygulama bileşeni (açılır bileşenin üst öğesi olan) vardır:

  • Bu bileşen her tıklama etkinliğini yakalar ve aynı rxjs'de yayılır Konu ( userMenu )

İşte kod:

export class AppComponent {

    constructor( public subjects: SubjectsService) {
        document.addEventListener('click', () => this.onClick());
    }
    onClick( ) {
        this.subjects.userMenu.next({});
    }
}

Beni ne rahatsız ediyor:

  1. Bu bileşenler arasında konektör görevi gören küresel bir Konuya sahip olma fikrinden pek rahat hissetmiyorum.
  2. SetTimeout : Burada butona kullanıcı tıklama açılan gösteriyor ki eğer aksi ne olur, çünkü bu gereklidir:
    • Kullanıcı, açılır menüyü göstermek için düğmeyi (açılır bileşenin bir parçası olmayan) tıklar.
    • Açılır menü görüntülenir ve hemen userMenu konusuna abone olur .
    • Tıklama etkinliği, uygulama bileşenine ulaşır ve yakalanır
    • Uygulama bileşeni, userMenu konusunda bir olay yayar
    • Açılır bileşen, userMenu'da bu eylemi yakalar ve açılır menüyü gizler.
    • Sonunda açılır menü hiçbir zaman görüntülenmez.

Bu ayarlanan zaman aşımı, sorunu çözen mevcut JavaScript kod dönüşünün sonuna kadar aboneliği geciktirir, ancak bence çok zarif bir şekilde.

Daha temiz, daha iyi, daha akıllı, daha hızlı veya daha güçlü çözümler biliyorsanız, lütfen bana bildirin :)!


Yanıtlar:


245

(document:click)Etkinliği kullanabilirsiniz :

@Component({
  host: {
    '(document:click)': 'onClick($event)',
  },
})
class SomeComponent() {
  constructor(private _eref: ElementRef) { }

  onClick(event) {
   if (!this._eref.nativeElement.contains(event.target)) // or some similar check
     doSomething();
  }
}

Başka bir yaklaşım, direktif olarak özel etkinlik oluşturmaktır. Ben Nadel tarafından gönderilen bu mesajlara göz atın:


1
@Sasxa teşekkür ederim ve kabul etti. Kullanımdan kaldırılmış bir API dokümanı varsa, beni buraya getiren aramada görüneceğini anladım.
danludwig

4
Event.target, [innerHTML] bağlaması gibi bir şey aracılığıyla dinamik olarak eklenen bir öğeyse, elementRef'in nativeElement öğesi onu içermez.
Patrick Graham

8
Bu tekniğin tek dezavantajı, şimdi uygulamanızda her tıkladığınızda tetiklenen bir tıklama olay dinleyicisine sahip olmanızdır.
codeepic

37
Resmi Angular 2 stil rehberine göre, dekoratördeki özellik @HostListener('document:click', ['$event'])yerine kullanmalısınız . hostComponent
Michał Miszczyszyn

15
ya da bunun için rxjs kullanabilirsiniz Observable.fromEvent(document, 'click').subscribe(event => {your code here}), böylece her zaman sadece örneğin açılır listeyi açmanız gerektiğinde abone olabilirsiniz ve kapattığınızda aboneliği iptal edebilirsiniz
Blind Despair

43

ZARİF YÖNTEMİ

Bunu buldum clickOut yönergeyi : https://github.com/chliebel/angular2-click-outside . Kontrol ediyorum ve iyi çalışıyor (sadece clickOutside.directive.tsprojeme kopyalarım ). U bu şekilde kullanabilirsiniz:

<div (clickOutside)="close($event)"></div>

closeKullanıcı div dışını tıkladığında çağrılacak işleviniz nerede . Söz konusu sorunu ele almanın çok zarif bir yoludur.

Açılır pencereyi kapatmak için yukarıdaki yönergeyi kullanırsanız, ilk olarak açılır pencereyi event.stopPropagation()açan düğme tıklama olay işleyicisine eklemeyi unutmayın.

BONUS:

Aşağıda oryginal yönergesi kodunu dosyadan kopyalarım clickOutside.directive.ts(bağlantı gelecekte çalışmayı durduracaksa) - yazar Christian Liebel :


2
@Vega Tavsiyem Direktifi * ngIf olan bir öğede kullanmak olacaktır, açılan durumlarda bu gibi olabilir<div class="wrap" *ngIf="isOpened" (clickOutside)="...// this should set this.isOpen=false"
Gabriel Balsa

19

Ben bu şekilde yaptım.

Belgeye bir olay dinleyicisi eklendi clickve bu işleyiciye containeriçerip içermediği kontrol edildi event.target- açılır listeyi gizleyin.

Böyle görünecektir.

@Component({})
class SomeComponent {
    @ViewChild('container') container;
    @ViewChild('dropdown') dropdown;

    constructor() {
        document.addEventListener('click', this.offClickHandler.bind(this)); // bind on doc
    }

    offClickHandler(event:any) {
        if (!this.container.nativeElement.contains(event.target)) { // check click origin
            this.dropdown.nativeElement.style.display = "none";
        }
    }
}

Selam. Bağlama (bu) gerekli mi?
Drenai

1
@Brian Gerekli olabilir veya olmayabilir, ancak this.offClickHandlerok fonksiyonuna sarılmışsa kesinlikle olmaz .
Lansana Camara

17

Sasxa'nın çoğu insan için cevap çalışmalarını kabul ettiğini düşünüyorum. Ancak, tıklama dışı olayları dinlemesi gereken Öğe içeriğinin dinamik olarak değiştiği bir durumum vardı. Bu nedenle Elements nativeElement öğesi, dinamik olarak oluşturulduğunda event.target öğesini içermedi. Bunu aşağıdaki yönergeyle çözebilirim

@Directive({
  selector: '[myOffClick]'
})
export class MyOffClickDirective {

  @Output() offClick = new EventEmitter();

  constructor(private _elementRef: ElementRef) {
  }

  @HostListener('document:click', ['$event.path'])
  public onGlobalClick(targetElementPath: Array<any>) {
    let elementRefInPath = targetElementPath.find(e => e === this._elementRef.nativeElement);
    if (!elementRefInPath) {
      this.offClick.emit(null);
    }
  }
}

ElementRef öğesinin event.target içerip içermediğini kontrol etmek yerine, elementRef'in olayın yolunda (hedeflenen DOM yolu) olup olmadığını kontrol ederim. Bu şekilde dinamik olarak oluşturulmuş Öğeleri işlemek mümkündür.


Teşekkürler - bu, çocuk bileşenleri mevcut olduğunda daha iyi çalışır
MAhsan

Bu benim için çok yardımcı oldu. bileşen tıklama dışında neden diğer yanıtlarla algılanmadığından emin değilim.
JavaQuest

13

Bunu iOS'ta yapıyorsanız touchstartetkinliği de kullanın :

Açısal 4'ten itibaren, süslemenin HostListenerbunu yapmanın tercih edilen yolu

import { Component, OnInit, HostListener, ElementRef } from '@angular/core';
...
@Component({...})
export class MyComponent implement OnInit {

  constructor(private eRef: ElementRef){}

  @HostListener('document:click', ['$event'])
  @HostListener('document:touchstart', ['$event'])
  handleOutsideClick(event) {
    // Some kind of logic to exclude clicks in Component.
    // This example is borrowed Kamil's answer
    if (!this.eRef.nativeElement.contains(event.target) {
      doSomethingCool();
    }
  }

}

10

Bugün işte benzer bir sorun üzerinde çalışıyoruz, bir açılır div'ın tıklandığında nasıl kaybolduğunu anlamaya çalışıyoruz. Bizimki ilk afişin sorusundan biraz farklı çünkü farklı bir bileşen veya yönergeden tıklamak istemedik , sadece belirli div'in dışındayız.

(Window: mouseup) olay işleyicisini kullanarak çözdük.

Adımlar:
1.) Tüm açılır menüye div'a benzersiz bir sınıf adı verdik.

2.) İç açılır menünün kendisinde (menüyü kapatmak DEĞİL tıklamaları istediğimiz tek bölüm) bir (window: mouseup) olay işleyicisi ekledik ve $ etkinliğini geçtik.

NOT: Üst tıklama işleyicisiyle çakıştığından, tipik bir "tıklama" işleyicisiyle yapılamadı.

3.) Denetleyicimizde, tıklama etkinliğinde çağırılmasını istediğimiz yöntemi oluşturduk ve tıklanan noktanın hedeflenen sınıf div'imizde olup olmadığını öğrenmek için event.closest ( burada dokümanlar ) kullanıyoruz.

 autoCloseForDropdownCars(event) {
        var target = event.target;
        if (!target.closest(".DropdownCars")) { 
            // do whatever you want here
        }
    }
 <div class="DropdownCars">
   <span (click)="toggleDropdown(dropdownTypes.Cars)" class="searchBarPlaceholder">Cars</span>
   <div class="criteriaDropdown" (window:mouseup)="autoCloseForDropdownCars($event)" *ngIf="isDropdownShown(dropdownTypes.Cars)">
   </div>
</div>


Ana makine dekoratörü içinde "window: mouseup" kullanılmalıdır.
Shivam

@ Shivam-- Ne demek istediğinden emin değilim. Daha fazla açıklayabilir misiniz? Teşekkürler!
Paige Bolduc

Yani doğrudan "pencere" nesnesini kullanmak yerine, bileşen dekoratör / "HostListener" dekoratör "host" özelliğini kullanmalısınız. Açısal 2'de "pencere" veya "belge" nesnesiyle çalışırken bu standart bir uygulamadır.
Shivam

2
Tarayıcı uyumluluğuna dikkat edin, .closest()bugün itibariyle IE / Edge'de desteklenmiyor ( caniuse )
superjos

5

Görünmez olabilecek tüm ekranı kapsayan ve yalnızca tıklama etkinliklerini yakalamak için orada bulunan açılır listeye bir kardeş öğe oluşturabilirsiniz. Ardından, o öğedeki tıklamaları algılayabilir ve tıklandığında açılır menüyü kapatabilirsiniz. Diyelim ki element sınıf serigrafi, bunun için bir stil:

.silkscreen {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1;
}

Z-endeksinin, açılır listeniz dışındaki her şeyin üzerine yerleştirilebilecek kadar yüksek olması gerekir. Bu durumda açılır listem z-endeksi 2 olur.

Diğer cevaplar benim için bazı durumlarda işe yaradı, ancak bazen içindeki öğelerle etkileşime girdiğimde açılanım kapanıyordu ve bunu istemedim. Beklediğim gibi olay hedefine göre bileşenimde bulunmayan öğeleri dinamik olarak ekledim. Bu karışıklığı çözmek yerine, sadece serigrafi yolunu deneyeceğim.


5

Herhangi bir çözüm bulamadım. Ben sadece belge ekledim: aşağıdaki gibi benim geçiş işlevine tıklayın:

    @Direktif({
      seçici: '[appDropDown]'
    })
    export class DropdownDirective OnInit {

      @HostBinding ('class.open') isOpen: boolean;

      yapıcı (private elemRef: ElementRef) {}

      ngOnInit (): void {
        this.isOpen = yanlış;
      }

      @HostListener ('belge: tıklayın', ['$ etkinlik'])
      @HostListener ('belge: dokunmatik başlangıç', ['$ etkinlik'])
      geçiş (etkinlik) {
        if (this.elemRef.nativeElement.contains (event.target)) {
          this.isOpen =! this.isOpen;
        } Başka {
          this.isOpen = yanlış;
      }
    }

Dolayısıyla, direktifimin dışında olduğumda, açılır listeyi kapatıyorum.


4
import { Component, HostListener } from '@angular/core';

@Component({
    selector: 'custom-dropdown',
    template: `
        <div class="custom-dropdown-container">
            Dropdown code here
        </div>
    `
})
export class CustomDropdownComponent {
    thisElementClicked: boolean = false;

    constructor() { }

    @HostListener('click', ['$event'])
    onLocalClick(event: Event) {
        this.thisElementClicked = true;
    }

    @HostListener('document:click', ['$event'])
    onClick(event: Event) {
        if (!this.thisElementClicked) {
            //click was outside the element, do stuff
        }
        this.thisElementClicked = false;
    }
}

DOWNSIDES: - Sayfadaki bu bileşenlerin her biri için iki tıklama olay dinleyicisi. Bunu sayfadaki bileşenlerde yüzlerce kez kullanmayın.


Hayır, sadece masaüstü tarayıcısında kullandım.
Alex Egli

3

@Tony yanıtını tamamlamak istiyorum, çünkü bileşen bileşen dışındaki tıklamadan sonra etkinlik kaldırılmıyor. Tam makbuz:

  • Ana öğenizi #container ile işaretleyin

    @ViewChild('container') container;
    
    _dropstatus: boolean = false;
    get dropstatus() { return this._dropstatus; }
    set dropstatus(b: boolean) 
    {
        if (b) { document.addEventListener('click', this.offclickevent);}
        else { document.removeEventListener('click', this.offclickevent);}
        this._dropstatus = b;
    }
    offclickevent: any = ((evt:any) => { if (!this.container.nativeElement.contains(evt.target)) this.dropstatus= false; }).bind(this);
  • Tıklanabilir öğede şunları kullanın:

    (click)="dropstatus=true"

Artık açılır durumunuzu dropstatus değişkeni ile kontrol edebilir ve [ngClass] ile uygun sınıfları uygulayabilirsiniz ...


3

Direktif yazabilirsiniz:

@Directive({
  selector: '[clickOut]'
})
export class ClickOutDirective implements AfterViewInit {
  @Input() clickOut: boolean;

  @Output() clickOutEvent: EventEmitter<any> = new EventEmitter<any>();

  @HostListener('document:mousedown', ['$event']) onMouseDown(event: MouseEvent) {

       if (this.clickOut && 
         !event.path.includes(this._element.nativeElement))
       {
           this.clickOutEvent.emit();
       }
  } 


}

Bileşeninizde:

@Component({
  selector: 'app-root',
  template: `
    <h1 *ngIf="isVisible" 
      [clickOut]="true" 
      (clickOutEvent)="onToggle()"
    >{{title}}</h1>
`,
  styleUrls: ['./app.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
  title = 'app works!';

  isVisible = false;

  onToggle() {
    this.isVisible = !this.isVisible;
  }
}

Html öğesi DOM'da bulunduğunda ve [clickOut] input özelliği 'true' olduğunda bu direktif emit olayı. Öğenin DOM'dan kaldırılmasından önce etkinliği işlemek için fareyle üzerine gelen etkinliği dinler.

Ve bir not: firefox, yol oluşturmak için işlevi kullanabileceğiniz olayda 'path' özelliğini içermiyor:

const getEventPath = (event: Event): HTMLElement[] => {
  if (event['path']) {
    return event['path'];
  }
  if (event['composedPath']) {
    return event['composedPath']();
  }
  const path = [];
  let node = <HTMLElement>event.target;
  do {
    path.push(node);
  } while (node = node.parentElement);
  return path;
};

Bu nedenle, direktif üzerindeki olay işleyicisini değiştirmelisiniz: event.path değiştirilmelidir getEventPath (event)

Bu modül yardımcı olabilir. https://www.npmjs.com/package/ngx-clickout Aynı mantığı içerir, ancak esc olayını kaynak html öğesinde de işler.


3

Doğru cevabın bir sorunu var, popover'ınızda bir clicakble bileşeniniz varsa, öğe artık containyöntemde olmayacak ve kapanacak, @ JuHarm89'a dayanarak kendim oluşturdum:

export class PopOverComponent implements AfterViewInit {
 private parentNode: any;

  constructor(
    private _element: ElementRef
  ) { }

  ngAfterViewInit(): void {
    this.parentNode = this._element.nativeElement.parentNode;
  }

  @HostListener('document:click', ['$event.path'])
  onClickOutside($event: Array<any>) {
    const elementRefInPath = $event.find(node => node === this.parentNode);
    if (!elementRefInPath) {
      this.closeEventEmmit.emit();
    }
  }
}

Yardım için teşekkürler!


2

@Tony mükemmel çözümü için daha iyi bir sürüm:

@Component({})
class SomeComponent {
    @ViewChild('container') container;
    @ViewChild('dropdown') dropdown;

    constructor() {
        document.addEventListener('click', this.offClickHandler.bind(this)); // bind on doc
    }

    offClickHandler(event:any) {
        if (!this.container.nativeElement.contains(event.target)) { // check click origin

            this.dropdown.nativeElement.closest(".ourDropdown.open").classList.remove("open");

        }
    }
}

Bir css dosyasında: // bootstrap açılır menüsünü kullanıyorsanız NOT NOT.

.ourDropdown{
   display: none;
}
.ourDropdown.open{
   display: inherit;
}

2

Bunun yerine mod kalıbını tıklayıp tıklamayacağınızı kontrol etmelisiniz, çok daha kolay.

Şablonunuz:

<div #modalOverlay (click)="clickOutside($event)" class="modal fade show" role="dialog" style="display: block;">
        <div class="modal-dialog" [ngClass]='size' role="document">
            <div class="modal-content" id="modal-content">
                <div class="close-modal" (click)="closeModal()"> <i class="fa fa-times" aria-hidden="true"></i></div>
                <ng-content></ng-content>
            </div>
        </div>
    </div>

Ve yöntem:

  @ViewChild('modalOverlay') modalOverlay: ElementRef;

// ... your constructor and other method

      clickOutside(event: Event) {
    const target = event.target || event.srcElement;
    console.log('click', target);
    console.log("outside???", this.modalOverlay.nativeElement == event.target)
    // const isClickOutside = !this.modalBody.nativeElement.contains(event.target);
    // console.log("click outside ?", isClickOutside);
    if ("isClickOutside") {
      // this.closeModal();
    }


  }

2

Bu benzer sorunu ele almak için bir direktif hazırladım ve Bootstrap kullanıyorum. Ancak benim durumumda, öğenin dışındaki tıklama olayını geçerli açılan açılır menüyü kapatmayı beklemek yerine, menüyü otomatik olarak kapatmak için 'mouseleave' olayını izlememiz daha iyi olur.

İşte benim çözümüm:

Direktif

import { Directive, HostListener, HostBinding } from '@angular/core';
@Directive({
  selector: '[appDropdown]'
})
export class DropdownDirective {

  @HostBinding('class.open') isOpen = false;

  @HostListener('click') toggleOpen() {
    this.isOpen = !this.isOpen;
  }

  @HostListener('mouseleave') closeDropdown() {
    this.isOpen = false;
  }

}

HTML

<ul class="nav navbar-nav navbar-right">
    <li class="dropdown" appDropdown>
      <a class="dropdown-toggle" data-toggle="dropdown">Test <span class="caret"></span>
      </a>
      <ul class="dropdown-menu">
          <li routerLinkActive="active"><a routerLink="/test1">Test1</a></li>
          <li routerLinkActive="active"><a routerLink="/test2/">Test2</a></li>
      </ul>
    </li>
</ul>

1

Bootstrap kullanıyorsanız, açılır listeler (Bootstrap bileşeni) aracılığıyla doğrudan bootstrap yolu ile yapabilirsiniz.

<div class="input-group">
    <div class="input-group-btn">
        <button aria-expanded="false" aria-haspopup="true" class="btn btn-default dropdown-toggle" data-toggle="dropdown" type="button">
            Toggle Drop Down. <span class="fa fa-sort-alpha-asc"></span>
        </button>
        <ul class="dropdown-menu">
            <li>List 1</li>
            <li>List 2</li>
            <li>List 3</li>
        </ul>
    </div>
</div>

Şimdi (click)="clickButton()"düğmelere bir şeyler koymak sorun değil . http://getbootstrap.com/javascript/#dropdowns


1

Ben de kendime ait bir çözüm buldum.

Ng-select eleman bileşenimde dinlediğim ve şu anda açık olan SelectComponent dışında açılan tüm diğer SelectComponent'leri kapatacak bir fonksiyonu çağıran bir (dropdownOpen) olayı oluşturdum .

Olayı yaymak için aşağıdaki gibi select.ts dosyasındaki bir işlevi değiştirdim :

private open():void {
    this.options = this.itemObjects
        .filter((option:SelectItem) => (this.multiple === false ||
        this.multiple === true && !this.active.find((o:SelectItem) => option.text === o.text)));

    if (this.options.length > 0) {
        this.behavior.first();
    }
    this.optionsOpened = true;
    this.dropdownOpened.emit(true);
}

HTML'de (dropdownOpened) için bir olay dinleyicisi ekledim :

<ng-select #elem (dropdownOpened)="closeOtherElems(elem)"
    [multiple]="true"
    [items]="items"
    [disabled]="disabled"
    [isInputAllowed]="true"
    (data)="refreshValue($event)"
    (selected)="selected($event)"
    (removed)="removed($event)"
    placeholder="No city selected"></ng-select>

Bu, ng2-select etiketine sahip bileşenin içindeki olay tetikleyicisindeki arama işlevimdir:

@ViewChildren(SelectComponent) selectElem :QueryList<SelectComponent>;

public closeOtherElems(element){
    let a = this.selectElem.filter(function(el){
                return (el != element)
            });

    a.forEach(function(e:SelectComponent){
        e.closeDropdown();
    })
}

1

NOT: Web çalışanlarını kullanmak isteyenler için ve belge ve nativeElement kullanmaktan kaçınmanız gerekir.

Aynı soruyu burada cevapladım: /programming/47571144

Yukarıdaki bağlantıdan Kopyala / Yapıştır:

Bir açılır menü ve bir onaylama iletişim kutusu oluştururken aynı sorunu yaşadım. Dışarı tıklarken bunları kapatmak istedim.

Son uygulamam mükemmel çalışıyor ancak bazı css3 animasyonları ve stil gerektiriyor.

NOT : Aşağıdaki kodu test etmedim, ütülenmesi gereken bazı sözdizimi sorunları, ayrıca kendi projeniz için bariz ayarlamalar olabilir!

Ben ne yaptım:

Yükseklik% 100, genişlik% 100 ve dönüşüm: ölçek (0) ile ayrı bir sabit div yaptım, bu aslında arka plan, arka plan rengi ile stil yapabilirsiniz: rgba (0, 0, 0, 0.466); menü açık ve arka plan tıklamak için tıklayın. Menü bir z-endeksini her şeyden daha yüksek bir hale getirir, ardından arka plan div, menüden daha düşük fakat aynı zamanda her şeyden daha yüksek bir z-endeksi alır. Ardından arka planda açılır menüyü kapatan bir tıklama etkinliği bulunur.

İşte html kodunuzla.

<div class="dropdownbackground" [ngClass]="{showbackground: qtydropdownOpened}" (click)="qtydropdownOpened = !qtydropdownOpened"><div>
<div class="zindex" [class.open]="qtydropdownOpened">
  <button (click)="qtydropdownOpened = !qtydropdownOpened" type="button" 
         data-toggle="dropdown" aria-haspopup="true" [attr.aria-expanded]="qtydropdownOpened ? 'true': 'false' ">
   {{selectedqty}}<span class="caret margin-left-1x "></span>
 </button>
  <div class="dropdown-wrp dropdown-menu">
  <ul class="default-dropdown">
      <li *ngFor="let quantity of quantities">
       <a (click)="qtydropdownOpened = !qtydropdownOpened;setQuantity(quantity)">{{quantity  }}</a>
       </li>
   </ul>
  </div>
 </div>

İşte bazı basit animasyonlar gerektiren css3.

/* make sure the menu/drop-down is in front of the background */
.zindex{
    z-index: 3;
}

/* make background fill the whole page but sit behind the drop-down, then
scale it to 0 so its essentially gone from the page */
.dropdownbackground{
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 2;
    transform: scale(0);
    opacity: 0;
    background-color: rgba(0, 0, 0, 0.466);
}

/* this is the class we add in the template when the drop down is opened
it has the animation rules set these how you like */
.showbackground{
    animation: showBackGround 0.4s 1 forwards; 

}

/* this animates the background to fill the page
if you don't want any thing visual you could use a transition instead */
@keyframes showBackGround {
    1%{
        transform: scale(1);
        opacity: 0;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}

Görsel bir şeyden sonra değilseniz, böyle bir geçiş kullanabilirsiniz.

.dropdownbackground{
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: 2;
    transform: scale(0);
    opacity: 0;
    transition all 0.1s;
}

.dropdownbackground.showbackground{
     transform: scale(1);
}

1

Odaklanma / bulanıklık olayıyla ilgili örneklerden esinlenerek başka bir çözüme rastladım.

Bu nedenle, global belge dinleyicisini eklemeden aynı işlevselliği elde etmek istiyorsanız, aşağıdaki örnek için geçerli olarak düşünebilirsiniz. Düğme odaklama olayının diğer işlemlerine rağmen, OSx'te Safari ve Firefox'ta da çalışır: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus

Açısal 8 ile Stackbiz üzerinde çalışma örneği: https://stackblitz.com/edit/angular-sv4tbi?file=src%2Ftoggle-dropdown%2Ftoggle-dropdown.directive.ts

HTML işaretlemesi:

<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" type="button" aria-haspopup="true" aria-expanded="false">Dropdown button</button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <a class="dropdown-item" href="#">Action</a>
    <a class="dropdown-item" href="#">Another action</a>
    <a class="dropdown-item" href="#">Something else here</a>
  </div>
</div>

Yönerge şöyle görünecektir:

import { Directive, HostBinding, ElementRef, OnDestroy, Renderer2 } from '@angular/core';

@Directive({
  selector: '.dropdown'
})
export class ToggleDropdownDirective {

  @HostBinding('class.show')
  public isOpen: boolean;

  private buttonMousedown: () => void;
  private buttonBlur: () => void;
  private navMousedown: () => void;
  private navClick: () => void;

  constructor(private element: ElementRef, private renderer: Renderer2) { }

  ngAfterViewInit() {
    const el = this.element.nativeElement;
    const btnElem = el.querySelector('.dropdown-toggle');
    const menuElem = el.querySelector('.dropdown-menu');

    this.buttonMousedown = this.renderer.listen(btnElem, 'mousedown', (evt) => {
      console.log('MOUSEDOWN BTN');
      this.isOpen = !this.isOpen;
      evt.preventDefault(); // prevents loose of focus (default behaviour) on some browsers
    });

    this.buttonMousedown = this.renderer.listen(btnElem, 'click', () => {
      console.log('CLICK BTN');
      // firefox OSx, Safari, Ie OSx, Mobile browsers.
      // Whether clicking on a <button> causes it to become focused varies by browser and OS.
      btnElem.focus();
    });

    // only for debug
    this.buttonMousedown = this.renderer.listen(btnElem, 'focus', () => {
      console.log('FOCUS BTN');
    });

    this.buttonBlur = this.renderer.listen(btnElem, 'blur', () => {
      console.log('BLUR BTN');
      this.isOpen = false;
    });

    this.navMousedown = this.renderer.listen(menuElem, 'mousedown', (evt) => {
      console.log('MOUSEDOWN MENU');
      evt.preventDefault(); // prevents nav element to get focus and button blur event to fire too early
    });
    this.navClick = this.renderer.listen(menuElem, 'click', () => {
      console.log('CLICK MENU');
      this.isOpen = false;
      btnElem.blur();
    });
  }

  ngOnDestroy() {
    this.buttonMousedown();
    this.buttonBlur();
    this.navMousedown();
    this.navClick();
  }
}

1

mouseleaveGörüşünüzde böyle kullanabilirsiniz

Açısal 8 ile test edin ve mükemmel çalışın

<ul (mouseleave)="closeDropdown()"> </ul>

Bu fare kapandığında kapları kapatacak, ancak varlığından habersiz olduğum için yine de paylaştığın için teşekkür ederim.
Ben Hayward

0

EN ZARİF YÖNTEMİ: D

Bunu yapmanın en kolay yolu var, bunun için direktiflere gerek yok.

"drop-element-that-toggle-your-drop" düğme etiketi olmalıdır. (Blur) özelliğinde herhangi bir yöntemi kullanın. Bu kadar.

<button class="element-that-toggle-your-dropdown"
               (blur)="isDropdownOpen = false"
               (click)="isDropdownOpen = !isDropdownOpen">
</button>

Açılır menüde tıklamayı açık tutmak istiyorsanız bu işe yaramaz, örneğin bir kullanıcı bir düğmeyi tıklayabilir
Evet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.