Reputation: 5025
I'm working on an Application with a lot of dropdowns, I would like to be able to close the dropdown whenever a click happens outside of this one.
I found some good solutions, but none of them handle the case of having a ngFor in it, when I log the click event target in the ngFor, I get the element but this one doesn't have any parent. I can not detect it with 'find' or 'contains' neither.
Does someone have a solution to detect if this target is part of the dropdown ?
the directive
import {
Directive,
ElementRef,
EventEmitter,
Input,
OnInit,
Output,
SimpleChange
} from '@angular/core';
@Directive({selector: '[clickOutside]'})
export class ClickOutside implements OnInit {
@Output() clickOutside:EventEmitter<Event> = new EventEmitter<Event>();
constructor(private _el:ElementRef) {
this.onClickBody = this.onClickBody.bind(this);
}
ngOnInit() {
document.body.addEventListener('click', this.onClickBody);
}
private onClickBody(e:Event) {
if (!this.isClickInElement(e)) {
this.clickOutside.emit(e);
}
}
private isClickInElement(e:any):boolean {
var current = e.target;
do {
console.log(current);
if ( current === this._el.nativeElement ) {
return( true );
}
current = current.parentNode;
} while ( current );
return false;
}
}
Example of where I call the directive
<div (clickOutside)="onClickedOutside($event)">
<ul>
<li *ngFor="let item of itemsList" (click)="selectItem(item)">
<span class="item">
{{item.name}}
</span>
</li>
</ul>
</div>
When I click on item.name, console.log(current); returns me two lines
<span>Item</span>
<li>
<span>Item</span>
</li>
Upvotes: 2
Views: 5267
Reputation: 5025
This solution works with Chrome but unfortunately not with IE. I'm still looking for another way to do it
private isClickInElement(e:any):boolean {
var current = e.target;
if(current == this._el.nativeElement) {
return true;
}
for(let parentKey in e.path) {
if(e.path[parentKey] == this._el.nativeElement) {
return true;
}
}
return false;
}
Upvotes: 1
Reputation: 657108
@Directive({selector: '[clickOutside]'})
export class ClickOutside implements OnInit {
@Output() clickOutside:EventEmitter<Event> = new EventEmitter<Event>();
constructor(private _eref: ElementRef) { }
@HostListener('window:click')
private onClickBody(e:Event) {
if (!this.isClickInElement(e)) {
this.clickOutside.emit(e);
}
}
private isClickInElement(e:any):boolean {
return this._eref.nativeElement.contains(event.target);
}
}
See also https://stackoverflow.com/a/35713421/217408
Upvotes: 2