最新情况:
在用户分享了最小可重现堆叠闪电战后,我在按钮上添加了类ignore-click
,然后在指令中忽略了按钮点击!
...
if (
!(
this.el.nativeElement?.contains(event.target) ||
event.target?.classList?.contains('ignore-click')
)
) {
this.clickedOutside.emit(true);
}
...
完整代码
双手
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { MenuComponent } from './app/menu/menu.component';
import { ClickOutsideDirective } from './app/click-outside.指令';
@Component({
selector: 'app-root',
imports: [MenuComponent, ClickOutsideDirective],
standalone: true,
template: `
<div class="container">
<div class="menu-container">
<button (click)="toggleMenu()" class="ignore-click">Toggle menu</button>
@if(isMenuOpen) {
<app-menu appClickOutside (clickedOutside)="isMenuOpen=false;"/>
}
</div>
</div>
`,
styles: `
.menu-container {
position: relative
}
.container {
display: flex;
justify-content: center;
align-items: center;
margin-top: 100px;
}
`,
})
export class App {
isMenuOpen = false;
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
}
bootstrapApplication(App);
指令
import {
Directive,
ElementRef,
EventEmitter,
HostListener,
Output,
} from '@angular/core';
@Directive({
selector: '[appClickOutside]',
standalone: true,
})
export class ClickOutsideDirective {
constructor(private el: ElementRef) {}
@Output() public clickedOutside = new EventEmitter();
@HostListener('document:click', ['$event'])
public onClick(event: any) {
if (
!(
this.el.nativeElement?.contains(event.target) ||
event.target?.classList?.contains('ignore-click')
)
) {
this.clickedOutside.emit(true);
}
}
@HostListener('document:keydown.escape', ['$event'])
onEscapeKeydownHandler(event: KeyboardEvent) {
this.clickedOutside.emit(true);
}
}
Working Stackblitz
它的发生,因为按钮点击也将被视为外部点击!!
Please pass in the button ref also to the 指令 and ignore the click if it originates from the button!
<button #buttonRef> this opens the menu!</button>
<div
appClickedOutside
[buttonRef]="buttonRef"
(clickedOutside)="menuClosed.emit(true)"
></div>
Then the 指令 can be changed to
import {
Input,
Directive,
ElementRef,
EventEmitter,
HostListener,
Output,
} from '@angular/core';
@Directive({
selector: '[appClickedOutside]',
standalone: true,
})
export class ClickedOutsideDirective {
@Input() buttonRef: ElementRef<any>;
constructor(private el: ElementRef) {}
@Output() public clickedOutside = new EventEmitter();
@HostListener('document:click', ['$event'])
public onClick(event: any) {
if (!(this.el.nativeElement.contains(event.target) &&
this.el.nativeElement === this.buttonRef.nativeElement)) { // <-changed here
this.clickedOutside.emit(true);
}
}
@HostListener('document:keydown.escape', ['$event'])
onEscapeKeydownHandler(event: KeyboardEvent) {
this.clickedOutside.emit(true);
}
}
因为没有stackblitz,所以很难调试这个问题.
甚至不需要传递按钮.你可以定义一个类,比如do-not-notice-this
,然后判断事件目标是否没有这个类!
<button class="do-not-notice-this"> this opens the menu!</button>
<div
appClickedOutside
(clickedOutside)="menuClosed.emit(true)"
></div>
the 指令 can be
import {
Directive,
ElementRef,
EventEmitter,
HostListener,
Output,
} from '@angular/core';
@Directive({
selector: '[appClickedOutside]',
standalone: true,
})
export class ClickedOutsideDirective {
constructor(private el: ElementRef) {}
@Output() public clickedOutside = new EventEmitter();
@HostListener('document:click', ['$event'])
public onClick(event: any) {
if (!(this.el.nativeElement.contains(event.target) &&
this.el.nativeElement.classList.contains('do-not-notice-this')) { // <-changed here
this.clickedOutside.emit(true);
}
}
@HostListener('document:keydown.escape', ['$event'])
onEscapeKeydownHandler(event: KeyboardEvent) {
this.clickedOutside.emit(true);
}
}