我已经实现了一个自定义Click Outside Directive,用于关闭模态对话框,通知,弹出窗口等(为了简单起见,让我们调用它们' popups')。但是,我正在努力为通过鼠标点击(即按钮点击)打开的弹出窗口和通过其他一些操作(即鼠标悬停)打开的弹出窗口实现通用解决方案。
以下是我面临的问题的Plunker example:鼠标悬停事件产生的弹出窗口需要两次点击才能关闭,同时弹出“点击我”的弹出窗口。按钮只需要点击一下就可以解除。
以下是该指令的代码:
@Directive({
selector: '[clickOutside]'
})
export class ClickOutsideDirective {
@Output('clickOutside') clickOutsideEvent = new EventEmitter();
private globalClick: Subscription;
constructor(private elRef: ElementRef) { }
ngOnInit() {
this.globalClick = Observable
.fromEvent(document, 'click')
.skip(1)
.subscribe((event: MouseEvent) => {
this.clicked(event);
});
}
ngOnDestroy() {
this.globalClick.unsubscribe();
}
private clicked(event: MouseEvent) {
let clickedInside = this.elRef.nativeElement.contains(event.target);
if (!clickedInside) {
this.clickOutsideEvent.emit();
}
}
}
在ngOnInit()内部,我初始化一个observable,它监听文档的点击并将事件传递给clicked()函数,该函数将检查是否在外部进行了点击,如果是,则引发事件。 observable使用skip运算符来过滤第一次单击事件。这是一种解决方法,因为如果您点击“点击我”按钮'按钮,原始点击事件是我想要跳过的事件,因为它对我在指令中没用。如果我不跳过它,弹出窗口会自动关闭,因为它会认为外面有一个点击。但是,这种解决方法有副作用,现在需要两次单击才能关闭弹出窗口,而弹出窗口是通过其他方式触发的,另一种是通过单击(Plunker中的Mouse Over示例演示了此问题)。
我正在努力想出一个优雅而通用的解决方案来解决这个问题。我可以想到多个&hacky'解决方案:
Hacky解决方案1: 我可以在“点击我”中停止事件传播。按钮,意味着ClickOutsideDirective将不会收到初始单击事件,我可以从文档中删除.skip运算符单击observable。这应该在理论上有效(没有尝试过这个),但我不喜欢的是指令的消费者现在需要知道他们需要停止事件传播,如果他们不遵守指令将不按设计工作。
Hacky解决方案2: 将布尔标志传递给指令,该指令可用于确定是否需要跳过第一次单击。很容易做到,但同样,API的用户现在需要知道他们需要设置此标志。如果他们忘记了,该指令将无法按预期工作。
Hacky解决方案3: 在初始化指令后,跳过在短时间内发生的第一个事件(让我们说100毫秒)。这也很容易做到,这个解决方案的优点是API的用户不需要向指令提供任何附加信息(或者停止任何事件传播),并且指令将只是工作' 。这里的问题是弄清楚在所有浏览器和硬件上使用的延迟。延迟需要不小于事件在实例化指令后到达所需的时间,但是如果用户决定在显示弹出窗口后立即点击,则不会大到跳过合法用户点击。
有人能想到一个更好,更简单,更优雅的解决方案吗?
答案 0 :(得分:0)
不是将逻辑保存在单独的指令中,为什么不将它保留在foundQues.questions.push(quesObj)
组件中,因为它是关闭它的原因。
更改组件,以便在弹出窗口后面有一个带有单击侦听器的固定位置div。然后,无论何时单击它,发送popup
事件。
clickOutside
这是一个有效的傻瓜:https://plnkr.co/edit/9QBJ0PXOg2GAUpBacgQV?p=preview
答案 1 :(得分:0)
所以我终于找到了答案。解决方案并不像我希望的那样干净,但基本上我添加了一个可观察的计时器(零值),它将触发.fromEvent(文档,'点击')可观察。我已经在所有现代浏览器(包括iPad和Android Chrome)中对此进行了测试,它似乎在任何地方都能正常运行。
ngOnInit() {
this.globalClick = Observable
.timer(0)
.switchMap(() => {
return Observable.fromEvent(this.document, 'click');
})
.subscribe((event: MouseEvent) => {
this.clicked(event);
});
}