Dropdown
A dropdown menu displays a list of actions and more with JavaScript dropdown plugin.
Contents
- Overview
- Installation
- Basic usage
- Accessibility notes
- Configuration Options
- JavaScript API
- Events
- Common Patterns
- License
Overview
The Dropdown component provides a menu that appears when triggered, displaying a list of actions or options. It supports multiple placement options, customizable triggers, keyboard navigation, and integrates with Floating UI for advanced positioning.
Key Features:
- Multiple placement options (top, bottom, left, right, auto)
- Customizable trigger events (click, hover, contextmenu)
- Floating UI integration for smart positioning
- Keyboard navigation support (Arrow keys, Home, End, Esc, A-Z)
- Programmatic control via JavaScript API
- Event system for lifecycle hooks
- Accessibility attributes (ARIA) built-in
Installation
To get started, install Dropdown plugin via npm, else you can skip this step if you are already using Preline UI as a package.
npm i @preline/dropdown
CSS
Use @source to register the plugin's JavaScript path for Tailwind CSS scanning, then @import the plugin's CSS files into your Tailwind CSS file.
@import "tailwindcss";
/* @preline/dropdown */
@source "../node_modules/@preline/dropdown/*.js";
@import "./node_modules/@preline/dropdown/variants.css";
@import "./node_modules/@preline/dropdown/theme.css";
JavaScript
Include the JavaScript that powers the interactive elements near the end of your </body> tag:
<script src="./node_modules/@preline/dropdown/index.js"></script>
Manual Initialization
Use the non-auto entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.
<script type="module">
import HSDropdown from "@preline/dropdown/non-auto.mjs";
new HSDropdown(document.querySelector("#dropdown"));
</script>
Via Bundler
When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.
@preline/dropdown is the auto-init entry: it scans the DOM and initializes matching elements automatically.
import "@preline/dropdown";
@preline/dropdown/non-auto is the manual entry: use it when you want explicit control over when initialization happens, either via autoInit() or by creating a specific instance yourself.
import HSDropdown from "@preline/dropdown/non-auto";
HSDropdown.autoInit();
// Or initialize a specific element manually
const el = document.querySelector("#dropdown");
if (el) new HSDropdown(el);
TypeScript
This package ships with TypeScript type definitions. No additional @types/ package is needed.
Basic usage
The following example demonstrates the minimal HTML structure required for a dropdown component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. The dropdown menu appears when clicking the button.
<div class="hs-dropdown relative inline-flex">
<button id="hs-dropdown-unstyled" type="button" class="hs-dropdown-toggle inline-flex justify-center items-center gap-x-2" aria-expanded="false" aria-label="Menu">
Actions
</button>
<div class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 w-56 hidden z-10 mt-2 min-w-60 bg-white" role="menu" aria-labelledby="hs-dropdown-unstyled">
<a class="block" href="#">Newsletter</a>
<a class="block" href="#">Purchases</a>
<a class="block" href="#">Downloads</a>
<a class="block" href="#">Team Account</a>
</div>
</div>
Structure Requirements:
hs-dropdown: Required container wrapperhs-dropdown-toggle: Required button element that triggers the dropdownhs-dropdown-menu: Required container for dropdown menu items- Proper ARIA attributes (
aria-expanded,aria-label,role="menu",aria-labelledby)
Initial State:
- Set
aria-expanded="false"on the toggle button initially - Add
hiddenclass to the dropdown menu initially - Menu visibility is controlled by
hs-dropdown-open:*modifier classes
Accessibility notes
Keyboard interactions
| Command | Description |
|---|---|
| Enter | Activates the selected menu item. |
| ArrowUp / ArrowDown | Focuses previous/next non-disabled item. |
| Home / End | Focuses first/last non-disabled item. |
| Esc | Closes any open dropdown menus. |
| A-Z / a-z | Focuses first item that matches keyboard input. |
Configuration Options
Data Attributes
Data attributes are added directly to HTML elements to configure dropdown behavior.
| Attribute | Target Element | Type | Default | Description |
|---|---|---|---|---|
data-hs-dropdown-transition |
hs-dropdown (container) |
- | - | Data attribute to designate the container to be animated. When present, enables transition animations for the dropdown menu. |
CSS Classes (Modifiers)
CSS class modifiers use Tailwind-style syntax with -- prefix to control dropdown behavior.
| Class Modifier | Target Element | Values | Default | Description |
|---|---|---|---|---|
[--placement:*] |
hs-dropdown (container) |
"auto" | "auto-start" | "auto-end" | "top" | "top-left" | "top-right" | "bottom" | "bottom-left" | "bottom-right" | "right" | "right-start" | "right-end" | "left" | "left-start" | "left-end" |
"bottom" |
Specifies the position of the menu when opened relative to the toggle button. |
[--scope:*] |
hs-dropdown (container) |
"window" | "parent" |
"parent" |
Determines whether the dropdown will be moved outside the parent, for correct display in elements with hidden overflow. Requires the Floating UI plugin. |
[--auto-close:*] |
hs-dropdown (container) |
"inside" | "outside" | "false" | "true" |
"true" |
Specifies the zone, when clicked, which will close the menu. inside closes only when clicking inside, outside closes only when clicking outside, true closes on any click, false prevents auto-close. |
[--has-autofocus:*] |
hs-dropdown (container) |
"true" | "false" |
"true" |
Disables autofocus on the first focusable element when opening a dropdown. Set to "false" to disable autofocus. |
[--strategy:*] |
hs-dropdown (container) |
"fixed" | "absolute" |
"fixed" |
Sets the position strategy. fixed positions relative to viewport, absolute positions relative to the nearest positioned ancestor. |
[--adaptive:*] |
hs-dropdown (container) |
"none" | "adaptive" |
"adaptive" |
Used to disable horizontal position calculations (useful for dropdown menus that span the full width of the screen) while still calculating the vertical position. |
[--gpu-acceleration:*] |
hs-dropdown (container) |
"true" | "false" |
"false" |
Disable/enable position calculation using the transform property. Set to "true" to enable GPU acceleration. |
[--offset:*] |
hs-dropdown (container) |
number | 10 |
Sets the dropdown's bottom or top offset in pixels. |
[--flip:*] |
hs-dropdown (container) |
"true" | "false" |
"true" |
Flips the menu's placement when it starts to overlap its reference element. Set to "false" to disable flipping. |
[--trigger:*] |
hs-dropdown (container) |
"hover" | "click" | "contextmenu" |
"click" |
Event to trigger a dropdown. hover shows on mouse enter, click shows on click, contextmenu shows on right-click. |
Example:
<div class="hs-dropdown --trigger:hover --placement:top --auto-close:false">
<button class="hs-dropdown-toggle">Hover me</button>
<div class="hs-dropdown-menu">Menu content</div>
</div>
Required CSS Classes
These classes define the structure and must be present for the dropdown to function.
| Class | Required On | Purpose |
|---|---|---|
hs-dropdown |
Container wrapper | Groups the toggle and menu elements together |
hs-dropdown-toggle |
Button element | The clickable element that activates the dropdown |
hs-dropdown-menu |
Menu container | The dropdown menu that appears on trigger |
Optional CSS Classes
| Class | Required On | Purpose |
|---|---|---|
hs-dropdown-toggle-wrapper |
Wrapper element | A wrapper for a Dropdown toggle, useful when some other element is placed in the Dropdown toggle. For example, if you want to place a "+" button inside an existing Dropdown toggle button that opens a modal. |
hs-dropdown-close |
Close element | Dropdown close element (can be multiple). Elements with this class will close the dropdown when clicked. |
Tailwind Modifiers
| Name | Description |
|---|---|
hs-dropdown-open:* |
The modifier that allows you to set Tailwind classes when the dropdown menu is open. |
JavaScript API
The HSDropdown object is available in the global window object after the plugin is loaded.
Instance Methods
These methods are called on a dropdown instance.
| Method | Parameters | Return Type | Description |
|---|---|---|---|
open(target, openedViaKeyboard) |
target: VirtualElement | HTMLElement (optional)openedViaKeyboard: boolean (optional) |
boolean |
Forces the dropdown menu to open programmatically. Returns true if opened successfully, false otherwise. |
close(isAnimated) |
isAnimated: boolean (optional) |
boolean |
Forces the dropdown menu to close programmatically. isAnimated indicates whether the dropdown menu is closed with animation. Returns true if closed successfully, false otherwise. |
forceClearState() |
None | void |
Destroys dropdown state without removing the instance. Use to reset dropdown to initial state. |
destroy() |
None | void |
Destroys the dropdown instance, removes all generated markup, classes, and event listeners. Use when removing dropdown from DOM. |
Static Methods
These methods are called directly on the HSDropdown class.
| Method | Parameters | Return Type | Description |
|---|---|---|---|
HSDropdown.getInstance(target, isInstance) |
target: HTMLElement | string (CSS selector)isInstance: boolean (optional) |
HSDropdown | { id: string | number, element: HSDropdown } | null |
Returns the dropdown instance associated with the target. If isInstance is true, returns collection item object { id, element } where element is the HSDropdown instance. If isInstance is false or omitted, returns the HSDropdown instance directly. Returns null if dropdown instance is not found. |
HSDropdown.open(target, openedViaKeyboard) |
target: HSDropdown | HTMLElement | string (CSS selector)openedViaKeyboard: boolean (optional) |
void |
Opens the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance open() method. |
HSDropdown.close(target) |
target: HSDropdown | HTMLElement | string (CSS selector) |
void |
Closes the dropdown identified by target. Accepts a dropdown instance, DOM element, or CSS selector string. Static alternative to instance close() method. |
Usage Examples
Example 1: Using instance methods (public API)
// Create a new dropdown instance
const dropdown = new HSDropdown(document.querySelector('#hs-dropdown'));
const openBtn = document.querySelector('#hs-open-btn');
// Open dropdown programmatically
openBtn.addEventListener('click', () => {
dropdown.open();
});
Example 2: Using static methods
const openBtn = document.querySelector('#hs-open-btn');
// Open dropdown using static method (no instance needed)
openBtn.addEventListener('click', () => {
HSDropdown.open('#hs-dropdown');
});
Example 3: Getting instance and using methods (recommended pattern)
// Get the dropdown instance
const instance = HSDropdown.getInstance('#hs-dropdown', true);
if (instance) {
const { element } = instance; // element is the HSDropdown instance
const openBtn = document.querySelector('#hs-open-btn');
// Use instance methods
openBtn.addEventListener('click', () => {
element.open();
});
// Clean up when removing from DOM
function removeDropdown() {
element.destroy();
}
}
Example 4: Destroying dropdown instance
const instance = HSDropdown.getInstance('#hs-dropdown', true);
if (instance) {
const { element } = instance;
const removeBtn = document.querySelector('#hs-remove-btn');
removeBtn.addEventListener('click', () => {
// Clean up before removing from DOM
element.destroy();
// Now safe to remove the element
document.querySelector('#hs-dropdown').remove();
});
}
Events
Dropdown instances emit events that can be listened to for lifecycle hooks and custom behavior.
| Event Name | When Fired | Callback Parameter | Description |
|---|---|---|---|
on:open |
After dropdown menu is opened | HSDropdown (dropdown instance) |
Fires after the dropdown menu has been displayed. Use for post-open actions like loading content or tracking analytics. |
on:close |
After dropdown menu is closed | HSDropdown (dropdown instance) |
Fires after the dropdown menu has been hidden. Use for cleanup or state updates. |
Event Usage Example
// Get dropdown instance
const instance = HSDropdown.getInstance('#hs-dropdown', true);
if (instance) {
const { element } = instance;
// Listen to open event
element.on('open', (dropdownInstance) => {
console.log('Dropdown opened:', dropdownInstance);
// Perform actions after dropdown opens
// e.g., load content, update UI, track analytics
});
// Listen to close event
element.on('close', (dropdownInstance) => {
console.log('Dropdown closed:', dropdownInstance);
// Perform cleanup or state updates
});
}
Common Patterns
Pattern 1: Hover Trigger
Show dropdown on hover instead of click.
<div class="hs-dropdown --trigger:hover">
<button class="hs-dropdown-toggle">Hover me</button>
<div class="hs-dropdown-menu">Menu content</div>
</div>
Pattern 2: Programmatic Control
Control dropdown from external buttons.
<div class="hs-dropdown" id="hs-dropdown-first">
<button class="hs-dropdown-toggle">Actions</button>
<div class="hs-dropdown-menu">Menu content</div>
</div>
<button id="hs-open-dropdown">Open Dropdown</button>
<button id="hs-close-dropdown">Close Dropdown</button>
<script>
const instance = HSDropdown.getInstance('#hs-dropdown-first', true);
if (instance) {
const { element } = instance;
document.querySelector('#hs-open-dropdown').addEventListener('click', () => {
element.open();
});
document.querySelector('#hs-close-dropdown').addEventListener('click', () => {
element.close();
});
}
</script>
License
Copyright (c) 2024 Htmlstream.
Licensed under the MIT License.