如何关闭外部点击的下拉菜单?


144

当用户单击下拉菜单之外的任何地方时,我想关闭我的登录菜单下拉菜单,我想使用Angular2和Angular2“方法”来完成此操作...

我已经实施了一个解决方案,但是我对此确实没有信心。我认为必须有一种最简单的方法来获得相同的结果,因此,如果您有任何想法...让我们讨论一下:)!

这是我的实现:

下拉组件:

这是我的下拉菜单的组件:

  • 每次将此组件设置为可见时,(例如:当用户单击按钮以显示它时)它将订阅存储在SubjectsService中的“全局” rxjs主题userMenu
  • 每次隐藏时,它都会退订该主题。
  • 该组件模板任何位置的每次点击都会触发onClick()方法,该方法只会停止将事件冒泡到顶部(和应用程序组件)

这是代码

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;
    }
}

应用程序组件:

另一方面,有应用程序组件(它是下拉组件的父级):

  • 该组件捕获每个click事件,并在相同的rxjs Subject(userMenu)上发出

这是代码:

export class AppComponent {

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

什么困扰我:

  1. 我对于让全局主题充当这些组件之间的连接器的想法感到非常不满意。
  2. setTimeout的:这是必要的,因为这里是,如果在此按钮,用户点击,显示的下拉什么,否则会发生:
    • 用户单击按钮(不是下拉组件的一部分)以显示下拉列表。
    • 显示该下拉列表,它立即订阅userMenu主题
    • 点击事件冒泡到应用程序组件并被捕获
    • 应用程序组件在userMenu主题上发出事件
    • 下拉组件在userMenu上捕获此操作,并隐藏下拉菜单。
    • 最后,从不显示下拉列表。

这个设置的超时将订阅延迟到当前JavaScript代码回合的末尾,从而解决了问题,但是我认为这是一种非常优雅的方式。

如果您知道更清洁,更好,更智能,更快或更强大的解决方案,请告诉我:)!


Answers:


245

您可以使用(document:click)事件:

@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();
  }
}

另一种方法是创建自定义事件作为指令。看看Ben Nadel的这些帖子:


1
@Sasxa谢谢,并同意。我发现如果有不被弃用的API文档,它将在导致我出现的搜索中显示。
danludwig

4
如果event.target是通过[innerHTML]绑定之类的东西动态添加的元素,则elementRef的nativeElement将不包含它。
帕特里克·格雷厄姆

8
该技术的唯一缺点是,现在您的应用程序中具有一个单击事件侦听器,该事件在每次单击时都会触发。
codeepic '17

37
根据官方的Angular 2样式指南,您应该在装饰器上使用@HostListener('document:click', ['$event'])而不是host属性Component
米哈尔Miszczyszyn

15
或者,您也可以使用rxjs之类的Observable.fromEvent(document, 'click').subscribe(event => {your code here}),这样您就可以始终仅在需要收听(例如您打开下拉菜单)时订阅,而在关闭时取消订阅
Blind Despair

42

优雅的方法

我找到了这个clickOut指令:https : //github.com/chliebel/angular2-click-outside。我检查了一下,效果很好(我只复制clickOutside.directive.ts到我的项目中)。您可以通过以下方式使用它:

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

close用户在div外部单击时将调用的函数在哪里。这是解决问题的一种非常优雅的方式。

如果使用上述指令关闭popUp窗口,请记住首先添加event.stopPropagation()到按钮单击事件处理程序中以打开popUp。

奖金:

下面我从文件中复制原始指令代码clickOutside.directive.ts(以防万一链接将来会停止工作)-作者是Christian Liebel


2
@Vega我的建议是在带有* ngIf的元素中使用该指令,如果是下拉菜单,则可能类似于<div class="wrap" *ngIf="isOpened" (clickOutside)="...// this should set this.isOpen=false"
Gabriel BalsaCantú18

19

我是这样做的。

在文档上添加了事件侦听器,click并在该处理程序中检查了我是否container包含event.target,如果不包含-隐藏下拉列表。

看起来像这样。

@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";
        }
    }
}

你好 .bind(this)是否必要?
德雷奈

1
@Brian可能有必要,也可能没有必要,但是如果他将包裹this.offClickHandler在箭头函数中,则绝对不是。
兰萨纳·卡马拉

17

我认为Sasxa接受的答案适用于大多数人。但是,我遇到了一种情况,即应侦听点击事件的Element内容会动态更改。因此,在动态创建时,Elements nativeElement不包含event.target。我可以使用以下指令解决此问题

@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是否包含event.target,而是检查elementRef是否在事件的路径(目标的DOM路径)中。这样就可以处理动态创建的元素。


谢谢-当存在子组件时,效果更好
MAhsan

这对我很有帮助。不知道为什么未在其他答案中检测到组件外部的点击。
JavaQuest

13

如果您在iOS上执行此操作,请同时使用以下touchstart事件:

从Angular 4开始,HostListener装饰是实现此目的的首选方法

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

今天,我们一直在处理一个类似的问题,试图找出如何使下拉div在单击时消失。我们的问题与最初发布者的问题略有不同,因为我们不想单击其他组件指令,而只是单击特定div之外的内容。

我们最终使用(window:mouseup)事件处理程序解决了这个问题。

步骤:
1.)我们为整个下拉菜单div赋予了唯一的类名称。

2.)在内部下拉菜单本身(我们想要单击的唯一部分以不关闭菜单)上,我们添加了一个(window:mouseup)事件处理程序,并传入$ event。

注意:无法使用典型的“单击”处理程序来完成,因为这与父单击处理程序冲突。

3.)在我们的控制器中,我们创建了我们希望在click out事件上调用的方法,然后使用event.closest(此处的文档)来查找被单击的点是否在目标类div内。

 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>


主机装饰器中应使用“ window:mouseup”。
Shivam

@Shivam-我不确定“在主机装饰器中使用”是什么意思。您能否进一步解释?谢谢!
Paige Bolduc

我的意思是应该直接使用组件装饰器的“ host”属性/组件的“ HostListener”装饰器,而不是直接使用“ window”对象。即标准实践使用“窗口”或在角2“文档”对象工作时
Shivam

2
只是注意浏览器的兼容性,到目前为止.closest(),IE / Edge尚不支持该浏览器(caniuse
superjos

5

您可以在下拉菜单中创建一个同级元素,以覆盖整个屏幕,该屏幕将是不可见的,并且仅用于捕获单击事件。然后,您可以检测到该元素上的单击,并在单击它时关闭下拉菜单。可以说该元素属于丝网印刷类,这里有一些样式:

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

Z索引必须足够高,才能将其定位在下拉菜单之外的所有其他位置。在这种情况下,我的下拉菜单将为b z-index 2。

其他答案在某些情况下对我有用,除了有时当我与其中的元素进行交互并且我不想要它时,我的下拉菜单关闭了。正如我期望的那样,根据事件目标,我动态地添加了未包含在组件中的元素。与其整理出混乱的东西,不如说我只是尝试丝网印刷。


5

我没有任何解决方法。我刚刚附加了文档:单击切换功能,如下所示:

    @指示({
      选择器:“ [appDropDown]”
    })
    导出类DropdownDirective实现OnInit {

      @HostBinding('class.open')isOpen:布尔值;

      构造函数(私有elemRef:ElementRef){}

      ngOnInit():void {
        this.isOpen = false;
      }

      @HostListener('document:click',['$ event'])
      @HostListener('document:touchstart',['$ event'])
      切换(事件){
        如果(this.elemRef.nativeElement.contains(event.target)){
          this.isOpen =!this.isOpen;
        }其他{
          this.isOpen = false;
      }
    }

因此,当我超出指令范围时,我将关闭下拉列表。


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;
    }
}

缺点:-每个页面上的每个组件都有两个click事件监听器。不要在页面上数百次的组件上使用它。


不,我只在桌面浏览器上使用过它。
Alex Egli

3

我想补充@Tony的答案,因为在组件外部单击后未删除该事件。完整收据:

  • 用#container标记您的主要元素

    @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);
  • 在clickable元素上,使用:

    (click)="dropstatus=true"

现在,您可以使用dropstatus变量控制下拉状态,并使用[ngClass]应用适当的类...


3

您可以编写指令:

@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();
       }
  } 


}

在您的组件中:

@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元素包含在DOM中并且[clickOut] input属性为'true'时,此指令发出事件。它侦听mousedown事件以处理事件,然后再将元素从DOM中删除。

还有一点要注意:firefox在事件中不包含属性“ path”,您可以使用函数创建路径:

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;
};

因此,您应该在指令上更改事件处理程序:应将event.path替换为getEventPath(event)

该模块可以提供帮助。https://www.npmjs.com/package/ngx-clickout 它包含相同的逻辑,但也处理源html元素上的esc事件。


3

正确答案有一个问题,如果您的弹窗中有一个clicakble组件,则该元素将不再存在于contain方法中,并且将基于我创建自己的@ JuHarm89而关闭:

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();
    }
  }
}

谢谢您的帮助!


2

@Tony更好的解决方案的更好版本:

@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");

        }
    }
}

在css文件中://如果使用bootstrap下拉列表,则不需要。

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

2

您应该检查是否单击模式叠加,这更加容易。

您的模板:

<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>

和方法:

  @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

我已经发出指令来解决这个类似的问题,并且我正在使用Bootstrap。但就我而言,与其等待元素外部的click事件关闭当前打开的下拉菜单,不如让我关注“ mouseleave”事件以自动关闭菜单,这会更好。

这是我的解决方案:

指示

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,则可以通过下拉菜单(Bootstrap组件)以引导方式直接进行操作。

<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>

现在可以(click)="clickButton()"在按钮上放东西了。 http://getbootstrap.com/javascript/#dropdowns


1

我也做了一些自己的解决方法。

我创建了一个(dropdownOpen)事件,该事件在ng-select元素组件上侦听并调用一个函数,该函数将关闭除当前打开的SelectComponent之外所有其他SelectComponent的打开。

我修改了select.ts文件中的一个函数,如下所示,以发出事件:

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中,我为(dropdownOpened)添加了一个事件侦听器:

<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>

这是我对具有ng2-select标记的组件内的事件触发器的调用函数:

@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

注意:对于那些想要使用Web Worker的用户,您需要避免使用document和nativeElement,这将起作用。

我在这里回答了同样的问题:https : //stackoverflow.com/questions/47571144

从以上链接复制/粘贴:

在制作下拉菜单和确认对话框时,我遇到了相同的问题,我想在单击外部时将其关闭。

我的最终实现效果完美,但是需要一些css3动画和样式。

注意:我尚未测试以下代码,可能需要解决一些语法问题,还需要针对您自己的项目进行明显的调整!

我做了什么:

我制作了一个单独的固定div,其高度为100%,宽度为100%,并进行了transform:scale(0),这实际上是背景,您可以使用background-color对其进行样式设置:rgba(0,0,0,0.466); 为了使菜单明显打开,并单击关闭背景。菜单的z-index值高于其他所有内容,然后背景div的z-index值低于菜单但也高于其他所有内容。然后,背景有一个点击事件,关闭了下拉菜单。

这是您的html代码。

<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>

这是需要一些简单动画的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;
    }
}

如果您不追求任何视觉效果,则可以使用这样的过渡

.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

我遇到了另一个解决方案,灵感来自于具有焦点/模糊事件的示例。

因此,如果要在不附加全局文档侦听器的情况下实现相同的功能,则可以将以下示例视为有效。尽管它们具有按钮焦点事件的其他处理方式,但它也可以在OSx上的Safari和Firefox中使用:https : //developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus

在角度为8的stackbiz上的工作示例:https ://stackblitz.com/edit/angular-sv4tbi?file = src%2Ftoggle-dropdown%2Ftoggle-dropdown.directive.ts

HTML标记:

<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>

该指令将如下所示:

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

您可以mouseleave像这样在您的视图中使用

使用角度8进行测试并完美工作

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

这将在鼠标离开时关闭容器,但是无论如何我还是感谢您的共享,因为我没有意识到它的存在。
本·海沃德

0

最优雅的方法:D

有一种最简单的方法,不需要任何指令。

“切换您的下拉菜单元素”应为按钮标签。在(blur)属性中使用任何方法。就这样。

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

如果您想使下拉菜单保持打开状态(例如用户可能会错过单击按钮)
无法
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.