• Skip to main content
  • Skip to primary sidebar

Web Development Archive

  • Archive
You are here: Home / Angular / Dynamic Dropdown Component with Close by Click Outside

Dynamic Dropdown Component with Close by Click Outside

export class DropdownComponent {
@Input() public isButton: boolean = false;
@Input() public isIconOnly: boolean = false;
@Input() public icon: string = 'icon-dots-vertical';
@Input() public appendTo: string | ElementRef | null = null;
@ViewChild('dropdownMenu') public dropdownMenu!: ElementRef;
@ViewChild(IconComponent, { read: ElementRef }) public iconElement!: ElementRef;

public ButtonGroup = ButtonGroup;
public ButtonIcon = ButtonIcon;
public ButtonType = ButtonType;

public isDropdownOpen = false;

public constructor(
private readonly _renderer: Renderer2,
private readonly _hostElement: ElementRef
) {}

public toggleDropdown(): void {
this.isDropdownOpen = !this.isDropdownOpen;

if (this.isDropdownOpen && this.appendTo) {
setTimeout(() => {
this.appendDropdownToTarget();
});
} else if (!this.isDropdownOpen) {
this.resetDropdownPosition();
}
}

public appendDropdownToTarget(): void {
const dropdownMenuElement = this.dropdownMenu.nativeElement;
let targetElement: HTMLElement | null = null;

if (this.appendTo === 'body') {
targetElement = document.body;
} else if (this.appendTo instanceof ElementRef) {
targetElement = this.appendTo.nativeElement;
} else if (typeof this.appendTo === 'string') {
targetElement = document.querySelector(this.appendTo);
}

if (targetElement) {
this._renderer.appendChild(targetElement, dropdownMenuElement);

const iconRect = this.iconElement.nativeElement.getBoundingClientRect();

const top = iconRect.bottom + window.scrollY;
const left = iconRect.left + window.scrollX;

this._renderer.setStyle(dropdownMenuElement, 'position', 'absolute');
this._renderer.setStyle(dropdownMenuElement, 'top', `${top}px`);
this._renderer.setStyle(dropdownMenuElement, 'left', `${left}px`);
this._renderer.setStyle(dropdownMenuElement, 'min-width', `${iconRect.width}px`);
}
}

public resetDropdownPosition(): void {
const dropdownMenuElement = this.dropdownMenu.nativeElement;
this._renderer.appendChild(this._hostElement.nativeElement, dropdownMenuElement);
}

@HostListener('document:click', ['$event.target'])
public onClickOutside(targetElement: HTMLElement): void {
const clickedInsideHost = this._hostElement.nativeElement.contains(targetElement);
const clickedInsideDropdown = this.dropdownMenu?.nativeElement.contains(targetElement);

if (!clickedInsideHost && !clickedInsideDropdown) {
this.isDropdownOpen = false;
}
}
}
<div class="dropdown-container">
<app-button
[label]="'SHARED.RUNNING_ADS' | transloco"
[buttonType]="ButtonType.SECONDARY"
[buttonGroup]="ButtonGroup.TEXT"
[buttonIcon]="ButtonIcon.ICON_RIGHT"
[icon]="'icon-chevron-down-2'"
[isWide]="true"
[isDisabled]="false"
[isDarkmode]="true"
(click)="toggleDropdown()"
/>
@if (isDropdownOpen) {
<div class="dropdown-menu">
<ng-content />
</div>
}
</div>
@use 'shared' as *;

.dropdown-container {
position: relative;
display: inline-block;
cursor: pointer;
}

.dropdown-menu {
border-radius: 8px;
box-shadow: 0px 16px 16px 0px rgba(217, 218, 243, 0.3);
background: var(--color-white);
border: 1px solid var(--color-grey-300);
position: absolute;
right: 0;
width: 360px;
z-index: 1000;
overflow: auto;
}

.dropdown-menu.show {
display: block;
}

Filed Under: Angular

About Gabor Flamich

I'm a web developer and designer based in Budapest, Hungary. In recent years, I've documented hundreds of solutions I came across during development. This site is an archive for useful code snippets on WordPress, Genesis Framework and WooCommerce. If You have any questions related to WordPress development, get in touch!

Primary Sidebar

  • angular.io
© 2026 WP Flames - All Right Reserved