检测单击外部元素但在父内部

时间:2018-02-16 02:29:45

标签: angular typescript navbar navigationbar angular-components

所以当你点击汉堡包图标本身以外的任何地方时,我正试图隐藏导航菜单。我让它工作,以便当您单击汉堡图标时,菜单在显示和隐藏之间切换。如果您单击页面本身,它也会隐藏菜单。问题是,如果您单击导航栏本身(但在汉堡包图标外),它将不会隐藏菜单。我想我明白这是因为handleClick()函数正在寻找不包含事件目标的引用元素,但我不知道如何解决这个问题并仍然使其他一切正常工作。

以下是组件HTML文件:

<nav class="navbar fixed-top navbar-dark bg-primary">
    <button class="navbar-toggler" type="button" (click)="toggleMenu()">
        <span class="navbar-toggler-icon"></span>
    </button>
    <a class="navbar-brand pull-right" href="#">Job Matrix</a>
</nav>
<div class="form-row btn-group-vertical nav-menu-fixed" *ngIf="visible" [@fade-in-out]="state">
    <a class="btn btn-primary text-left item" href="#" role="button">Jobs</a>
    <a class="btn btn-primary text-left item" href="#" role="button">Resumes</a>
</div>

这是角度组件打字稿文件:

import { Component, OnInit, ElementRef } from '@angular/core';
import { trigger, group, state, style, animate, transition } from '@angular/animations';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'

@Component({
    selector: 'nav-menu',
    templateUrl: './navmenu.component.html',
    styleUrls: ['./navmenu.component.scss'],
    host: {
        '(document:click)': 'handleClick($event)',
    },
    animations: [
        trigger('fade-in-out', [
            state('open', style({
                transform: 'translate3d(0, 0, 0)'
            })),
            state('closed', style({
                transform: 'translate3d(0, -110%, 0)'
            })),
            transition('closed => open', animate('500ms ease-in')),
            transition('open => closed', animate('500ms ease-out'))
        ])
    ]
})

export class NavMenuComponent implements OnInit {
    [x: string]: any;

    visible: boolean;
    state: string;

    constructor(private ref: ElementRef) {
    }

    ngOnInit(): void {
        this.visible = false;
        this.state = 'closed';
    }

    toggleMenu() {
        this.visible = true;
        setTimeout(() => {
            this.state = this.state == "open" ? 'closed' : 'open';
        }, 50);
    }

    handleClick(event: Event) {
        if (!this.ref.nativeElement.contains(event.target)) {
            this.state = 'closed';
            setTimeout(() => {
                this.visible = false;
            }, 500);
        }
    }

}

1 个答案:

答案 0 :(得分:0)

现在,ref正在为您提供整个主机元素(汉堡包和菜单),但似乎您要将菜单(.nav-menu-fixed)分开,如果点击位于菜单之外(包括.navbar-toggler),关闭菜单。您可以使用@ViewChild装饰器来获取菜单本身,并检查点击是否包含菜单。

将ref(#menu)添加到菜单组件中:

<div #menu class="nav-menu-fixed" *ngIf="visible" [@fade-in-out]="state">
  <a class="btn btn-primary text-left item" href="#" role="button">Jobs</a>
  <a class="btn btn-primary text-left item" href="#" role="button">Resumes</a>
</div>

然后使用@ViewChild装饰器查询DOM中的菜单元素。

export class NavMenuComponent implements OnInit {
    @ViewChild('menu', {read: ElementRef})
    menu: ElementRef;

    constructor() {}

    @HostListener('document:click', ['$event'])
    handleClick(event: Event) {
     const menuIsOpen = this.visible && this.state === 'open';
     const toggleWasClicked = this.toggle.nativeElement.contains(event.target);
     const menuWasClicked = this.menu && this.menu.nativeElement.contains(event.target);
     const pageWasClicked = !toggleWasClicked && !menuWasClicked;
     const menuShouldClose = (pageWasClicked || toggleWasClicked) && menuIsOpen;

     if (menuShouldClose) {
         this.state = 'closed';
         setTimeout(() => {
             this.visible = false;
         }, 500);
     }
    }    
}

我在我的代码段中使用了@HostListener装饰器而不是host属性,因为我认为将事件侦听器靠近处理它的方法是一种很好的方法:)