【问题标题】:Detecting Click Outside Element but Inside Parent检测点击外部元素但在父元素内部
【发布时间】:2018-02-16 02:29:44
【问题描述】:

因此,当您单击汉堡包图标本身以外的任何位置时,我试图能够隐藏导航菜单。我让它工作,以便当您单击汉堡包图标时菜单在显示和隐藏之间切换。如果您单击页面本身,它也会隐藏菜单。问题是,如果您单击导航栏本身(但在汉堡图标之外),它不会隐藏菜单。我想我理解这是因为 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);
        }
    }

}

【问题讨论】:

    标签: angular typescript navbar navigationbar angular-components


    【解决方案1】:

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

    向您的菜单组件添加一个引用 (#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);
         }
        }    
    }
    

    我在我的 sn-p 中使用了 @HostListener 装饰器而不是 host 属性,因为我认为这是让事件侦听器靠近处理它的方法的好方法 :)

    【讨论】:

    • 这在 handleClick() 函数的第一行(在 this.menu 上)给了我一个未定义的错误。
    • 您是否在组件类中定义了@ViewChild 属性?
    • 哦,我明白了,这是因为只有当visible 为真时,菜单才会出现在 DOM 中。您需要添加一个额外的检查,例如 this.menu &amp;&amp; this.menu.nativeElement...
    • 似乎 this.menu 总是未定义。因此,如果我检查未定义,那么单击导航栏本身时它仍然不会隐藏菜单。如果我不检查未定义,那么它会在显示菜单后立即隐藏菜单,因为汉堡菜单单击也会触发隐藏。我确实找到了一种方法,通过稍微更改 handleClick 函数使其与我之前的工作方式一致:
    • var clickedElement = this.ref.nativeElement; var parentElement = event.target.parentElement; if (!clickedElement.contains(event.target) || clickedElement == parentElement) { this.state = 'close'; setTimeout(() => { this.visible = false; }, 500); }
    猜你喜欢
    • 2016-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-11
    • 2023-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多