Created
August 31, 2021 11:31
-
-
Save saravanapriyanm/fbedc453a063aa17fa2cf2241add23ac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Directive, | |
ElementRef, | |
EventEmitter, | |
Inject, | |
Input, | |
OnChanges, | |
OnDestroy, | |
OnInit, | |
Output, | |
PLATFORM_ID, | |
SimpleChanges, | |
NgZone, | |
} from '@angular/core'; | |
import { isPlatformBrowser } from '@angular/common'; | |
// tslint:disable-next-line: directive-selector | |
@Directive({ selector: '[clickOutside]' }) | |
export class ClickOutsideDirective implements OnInit, OnChanges, OnDestroy { | |
@Input() clickOutsideEnabled = true; | |
@Input() attachOutsideOnClick = false; | |
@Input() delayClickOutsideInit = false; | |
@Input() emitOnBlur = false; | |
@Input() exclude = ''; | |
@Input() excludeBeforeClick = false; | |
@Input() clickOutsideEvents = ''; | |
@Output() clickOutside: EventEmitter<Event> = new EventEmitter<Event>(); | |
private nodesExcluded: Array<HTMLElement> = []; | |
private events: Array<string> = ['click']; | |
constructor( | |
private el: ElementRef, | |
private ngZone: NgZone, | |
@Inject(PLATFORM_ID) private platformId: object) { | |
this._initOnClickBody = this._initOnClickBody.bind(this); | |
this._onClickBody = this._onClickBody.bind(this); | |
this._onWindowBlur = this._onWindowBlur.bind(this); | |
} | |
ngOnInit() { | |
if (!isPlatformBrowser(this.platformId)) { return; } | |
this._init(); | |
} | |
ngOnDestroy() { | |
if (!isPlatformBrowser(this.platformId)) { return; } | |
this._removeClickOutsideListener(); | |
this._removeAttachOutsideOnClickListener(); | |
this._removeWindowBlurListener(); | |
} | |
ngOnChanges(changes: SimpleChanges) { | |
if (!isPlatformBrowser(this.platformId)) { return; } | |
if (changes.attachOutsideOnClick || changes.exclude || changes.emitOnBlur) { | |
this._init(); | |
} | |
} | |
private _init() { | |
if (this.clickOutsideEvents !== '') { | |
this.events = this.clickOutsideEvents.split(',').map(e => e.trim()); | |
} | |
this._excludeCheck(); | |
if (this.attachOutsideOnClick) { | |
this._initAttachOutsideOnClickListener(); | |
} else { | |
this._initOnClickBody(); | |
} | |
if (this.emitOnBlur) { | |
this._initWindowBlurListener(); | |
} | |
} | |
private _initOnClickBody() { | |
if (this.delayClickOutsideInit) { | |
setTimeout(this._initClickOutsideListener.bind(this)); | |
} else { | |
this._initClickOutsideListener(); | |
} | |
} | |
private _excludeCheck() { | |
if (this.exclude) { | |
try { | |
const nodes = Array.from(document.querySelectorAll(this.exclude)) as Array<HTMLElement>; | |
if (nodes) { | |
this.nodesExcluded = nodes; | |
} | |
} catch (err) { | |
console.error('[ng-click-outside] Check your exclude selector syntax.', err); | |
} | |
} | |
} | |
private _onClickBody(ev: Event) { | |
if (!this.clickOutsideEnabled) { return; } | |
if (this.excludeBeforeClick) { | |
this._excludeCheck(); | |
} | |
if (!this.el.nativeElement.contains(ev.target) && !this._shouldExclude(ev.target)) { | |
this._emit(ev); | |
if (this.attachOutsideOnClick) { | |
this._removeClickOutsideListener(); | |
} | |
} | |
} | |
/** | |
* Resolves problem with outside click on iframe | |
* @see https://github.com/arkon/ng-click-outside/issues/32 | |
*/ | |
private _onWindowBlur(ev: Event) { | |
setTimeout(() => { | |
if (!document.hidden) { | |
this._emit(ev); | |
} | |
}); | |
} | |
private _emit(ev: Event) { | |
if (!this.clickOutsideEnabled) { return; } | |
this.ngZone.run(() => this.clickOutside.emit(ev)); | |
} | |
private _shouldExclude(target): boolean { | |
for (const excludedNode of this.nodesExcluded) { | |
if (excludedNode.contains(target)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
private _initClickOutsideListener() { | |
this.ngZone.runOutsideAngular(() => { | |
this.events.forEach(e => document.addEventListener(e, this._onClickBody)); | |
}); | |
} | |
private _removeClickOutsideListener() { | |
this.ngZone.runOutsideAngular(() => { | |
this.events.forEach(e => document.removeEventListener(e, this._onClickBody)); | |
}); | |
} | |
private _initAttachOutsideOnClickListener() { | |
this.ngZone.runOutsideAngular(() => { | |
this.events.forEach(e => this.el.nativeElement.addEventListener(e, this._initOnClickBody)); | |
}); | |
} | |
private _removeAttachOutsideOnClickListener() { | |
this.ngZone.runOutsideAngular(() => { | |
this.events.forEach(e => this.el.nativeElement.removeEventListener(e, this._initOnClickBody)); | |
}); | |
} | |
private _initWindowBlurListener() { | |
this.ngZone.runOutsideAngular(() => { | |
window.addEventListener('blur', this._onWindowBlur); | |
}); | |
} | |
private _removeWindowBlurListener() { | |
this.ngZone.runOutsideAngular(() => { | |
window.removeEventListener('blur', this._onWindowBlur); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment