Skip to content

Commit

Permalink
perf(dropdown): reduce the number of document click listeners (#4605)
Browse files Browse the repository at this point in the history
* perf(dropdown): reduce the number of document click listeners

Edit document click listeners to be added only for open dropdowns which improves overall page performance in case of a large number of dropdowns.
  • Loading branch information
Dragsaw authored and Domainv committed Apr 3, 2019
1 parent fa25dd5 commit 4d49218
Showing 1 changed file with 39 additions and 25 deletions.
64 changes: 39 additions & 25 deletions src/dropdown/bs-dropdown-toggle.directive.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
ChangeDetectorRef,
Directive,
ElementRef,
HostBinding,
HostListener,
OnDestroy
OnDestroy,
Renderer2
} from '@angular/core';
import { Subscription } from 'rxjs';

import { Subscription } from 'rxjs';
import { BsDropdownState } from './bs-dropdown.state';
import { BsDropdownDirective } from './bs-dropdown.directive';

Expand All @@ -19,19 +21,50 @@ import { BsDropdownDirective } from './bs-dropdown.directive';
})
export class BsDropdownToggleDirective implements OnDestroy {
@HostBinding('attr.disabled') isDisabled: boolean = null;

// @HostBinding('class.active')
@HostBinding('attr.aria-expanded') isOpen: boolean;

private _subscriptions: Subscription[] = [];
private _documentClickListener: Function;
private _escKeyUpListener: Function;

constructor(private _state: BsDropdownState, private _element: ElementRef, private dropdown: BsDropdownDirective) {
constructor(
private _changeDetectorRef: ChangeDetectorRef,
private _dropdown: BsDropdownDirective,
private _element: ElementRef,
private _renderer: Renderer2,
private _state: BsDropdownState
) {
// sync is open value with state
this._subscriptions.push(
this._state.isOpenChange.subscribe(
(value: boolean) => (this.isOpen = value)
(value: boolean) => {
this.isOpen = value;

if (value) {
this._documentClickListener = this._renderer.listen('document', 'click', (event: any) => {
if (this._state.autoClose && event.button !== 2 &&
!this._element.nativeElement.contains(event.target) &&
!(this._state.insideClick && this._dropdown._contains(event))
) {
this._state.toggleClick.emit(false);
this._changeDetectorRef.detectChanges();
}
});

this._escKeyUpListener = this._renderer.listen(this._element.nativeElement, 'keyup.esc', () => {
if (this._state.autoClose) {
this._state.toggleClick.emit(false);
this._changeDetectorRef.detectChanges();
}
});
} else {
this._documentClickListener();
this._escKeyUpListener();
}
}
)
);

// populate disabled state
this._subscriptions.push(
this._state.isDisabledChange.subscribe(
Expand All @@ -48,25 +81,6 @@ export class BsDropdownToggleDirective implements OnDestroy {
this._state.toggleClick.emit(true);
}

@HostListener('document:click', ['$event'])
onDocumentClick(event: MouseEvent): void {
if (
this._state.autoClose &&
event.button !== 2 &&
!this._element.nativeElement.contains(event.target) &&
!(this._state.insideClick && this.dropdown._contains(event))
) {
this._state.toggleClick.emit(false);
}
}

@HostListener('keyup.esc')
onEsc(): void {
if (this._state.autoClose) {
this._state.toggleClick.emit(false);
}
}

ngOnDestroy(): void {
for (const sub of this._subscriptions) {
sub.unsubscribe();
Expand Down

0 comments on commit 4d49218

Please sign in to comment.