Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Multi-Level Flyout Menu

Bootstrap 5 + jQuery compatible multi-level flyout menu for widget integration with full dark mode support.

📁 Files

  • css/widget-flyout.css - Flyout menu styles with dark mode support
  • js/widget-flyout.js - Flyout menu functionality with theme utilities
  • index.html - Main demo page with live examples and theme toggle
  • example-dark-mode.html - Dedicated dark mode demonstration
  • README.md - This file (complete documentation)

✨ Features

  • ✅ Unlimited nesting depth
  • ✅ RTL/LTR support via dir attribute
  • ✅ Touch/hover detection (hover on desktop, click on mobile)
  • ✅ Fixed positioning (submenus never hidden behind content)
  • ✅ Keyboard navigation (Arrow keys, Enter, Escape)
  • ✅ ARIA attributes for accessibility
  • ✅ JSON data or pre-rendered HTML modes
  • ✅ Multiple widgets per page (no conflicts)
  • ✅ One submenu open at a time (siblings auto-close)
  • ✅ Viewport edge detection
  • Dark mode support with Bootstrap theme integration

📚 Table of Contents

  1. Quick Start
  2. Dark Mode
  3. API Reference
  4. RTL Support
  5. Keyboard Navigation
  6. Customization
  7. Advanced Usage
  8. Troubleshooting
  9. Browser Support
  10. Examples

🚀 Quick Start

1. Include Dependencies

<!-- Bootstrap 5 CSS --><linkhref="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"><!-- Bootstrap Icons --><linkhref="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet"><!-- Widget Flyout CSS --><linkhref="css/widget-flyout.css" rel="stylesheet"><!-- jQuery 3.7+ --><scriptsrc="https://code.jquery.com/jquery-3.7.1.min.js"></script><!-- Bootstrap 5 JS Bundle --><scriptsrc="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script><!-- Widget Flyout JS --><scriptsrc="js/widget-flyout.js"></script>

2. Widget Structure

<divclass="widget" dir="ltr"><divclass="widget-header bg-danger text-white d-flex justify-content-between align-items-center px-3 py-2"><spanclass="widget-title">Categories</span><iclass="bi bi-grid-3x3-gap"></i></div><divclass="widget-body p-3" id="my-menu"></div></div>

3. Initialize Menu

Option A: JSON Data

constdata=[{label: 'Electronics',icon: 'bi bi-laptop',url: '#',children: [{label: 'Laptops',icon: 'bi bi-laptop',url: '/laptops'},{label: 'Phones',icon: 'bi bi-phone',url: '/phones'}]},{label: 'Books',icon: 'bi bi-book',url: '/books'}];renderFlyoutFromJson('#my-menu',data);

Option B: Existing HTML

initWidgetFlyout('#my-menu');

🌙 Dark Mode

Quick Setup

// Initialize theme (auto-detects system preference)FlyoutTheme.init();// Toggle between light and darkFlyoutTheme.toggle();// Set specific themeFlyoutTheme.setTheme('dark');// or 'light' or 'auto'

Dark Mode API

FlyoutTheme.init()

Initialize theme from localStorage or system preference.

FlyoutTheme.init();
  • Checks localStorage for saved preference
  • Falls back to system preference if no saved value
  • Applies the theme automatically
  • Returns the applied theme ('light' or 'dark')

FlyoutTheme.setTheme(theme)

Set a specific theme.

FlyoutTheme.setTheme('dark');// Set dark modeFlyoutTheme.setTheme('light');// Set light modeFlyoutTheme.setTheme('auto');// Use system preference

Parameters:

  • theme (string): 'light', 'dark', or 'auto'

Returns: The applied theme ('light' or 'dark')

FlyoutTheme.toggle()

Toggle between light and dark modes.

FlyoutTheme.toggle();

Returns: The new theme ('light' or 'dark')

FlyoutTheme.getTheme()

Get the current active theme.

constcurrentTheme=FlyoutTheme.getTheme();console.log(currentTheme);// 'light' or 'dark'

FlyoutTheme.watchSystemTheme()

Watch for system theme preference changes.

FlyoutTheme.watchSystemTheme();

Theme Toggle Button Example

<buttontype="button" class="btn btn-primary" id="themeToggle"><iclass="bi bi-moon-stars-fill"></i></button><script>$(document).ready(function(){FlyoutTheme.init();FlyoutTheme.watchSystemTheme();$('#themeToggle').on('click',function(){FlyoutTheme.toggle();// Update iconconsttheme=FlyoutTheme.getTheme();consticon=theme==='dark' ? 'bi-sun-fill' : 'bi-moon-stars-fill';$(this).find('i').attr('class',`bi ${icon}`);});});</script>

CSS Variables for Dark Mode

:root {
/* Light mode colors (default) */--flyout-bg:#ffffff;
--flyout-border-color:#dee2e6;
--flyout-text-color:#212529;
--flyout-hover-bg:#f8f9fa;
--flyout-hover-text:#212529;
--flyout-active-bg:#e9ecef;
--flyout-focus-outline:#0d6efd;
--flyout-shadow:rgba(0,0,0,0.15);
}
[data-bs-theme="dark"] {
/* Dark mode colors */--flyout-bg:#212529;
--flyout-border-color:#495057;
--flyout-text-color:#dee2e6;
--flyout-hover-bg:#343a40;
--flyout-hover-text:#ffffff;
--flyout-active-bg:#495057;
--flyout-focus-outline:#0d6efd;
--flyout-shadow:rgba(0,0,0,0.5);
}

Custom Theme Example

/* Custom dark theme with blue tones */
[data-bs-theme="dark"] {
--flyout-bg:#1a1f35;
--flyout-border-color:#2d3555;
--flyout-text-color:#e8eaed;
--flyout-hover-bg:#2d3555;
--flyout-hover-text:#ffffff;
--flyout-active-bg:#3d4565;
--flyout-focus-outline:#4a9eff;
--flyout-shadow:rgba(0,0,0,0.6);
}

🎯 API Reference

Menu Initialization

renderFlyoutFromJson(container, data, options)

Render menu from JSON data.

renderFlyoutFromJson('#menu-container',menuData,{hoverDelay: 200,closeOnClickOutside: true,keyboardNav: true});

Parameters:

  • container (string|element): Selector or DOM element
  • data (array): Menu data structure
  • options (object): Optional configuration

initWidgetFlyout(container, options)

Initialize existing HTML menu structure.

initWidgetFlyout('#menu-container',{hoverDelay: 200,closeOnClickOutside: true,keyboardNav: true});

Data Structure

{label: 'Text',// Required - menu item texticon: 'bi bi-icon',// Optional - icon classurl: '#',// Optional - link URLonClick: function(item,event){},// Optional - click handlerchildren: []// Optional - sub-items array}

Options

{hoverDelay: 200,// Hover delay in millisecondscloseOnClickOutside: true,// Close menu when clicking outsidekeyboardNav: true// Enable keyboard navigation}

Complete Example

constmenuData=[{label: 'Home',icon: 'bi bi-house',url: '/home',children: [{label: 'Dashboard',icon: 'bi bi-speedometer2',url: '/dashboard'},{label: 'Settings',icon: 'bi bi-gear',url: '/settings',children: [{label: 'Profile',icon: 'bi bi-person',url: '/profile'},{label: 'Security',icon: 'bi bi-shield-lock',url: '/security'}]}]},{label: 'Search',icon: 'bi bi-search',onClick: function(item,e){e.preventDefault();alert('Search clicked!');}}];$(document).ready(function(){// Initialize themeFlyoutTheme.init();// Render menurenderFlyoutFromJson('#my-menu',menuData,{hoverDelay: 150,closeOnClickOutside: true,keyboardNav: true});});

🌍 RTL Support

Basic RTL Setup

<divclass="widget" dir="rtl"><divclass="widget-header bg-danger text-white d-flex justify-content-between align-items-center px-3 py-2"><spanclass="widget-title">دسته‌ها</span><iclass="bi bi-grid-3x3-gap"></i></div><divclass="widget-body p-3" id="rtl-menu"></div></div><script>constpersianData=[{label: 'خانه',icon: 'bi bi-house',url: '#',children: [{label: 'داشبورد',icon: 'bi bi-speedometer2',url: '#'}]}];renderFlyoutFromJson('#rtl-menu',persianData);</script>

The library automatically detects RTL direction from:

  • dir="rtl" attribute on parent elements
  • dir="rtl" on <html> element
  • Adapts caret direction and menu positioning automatically

⌨️ Keyboard Navigation

Keyboard Shortcuts

KeyAction (LTR)Action (RTL)
Move to next itemMove to next item
Move to previous itemMove to previous item
Open submenuClose submenu
Close submenuOpen submenu
EnterActivate itemActivate item
SpaceActivate itemActivate item
EscClose current submenuClose current submenu

Accessibility Features

  • Full ARIA attribute support
  • Screen reader compatible
  • Keyboard-only navigation
  • Focus indicators visible
  • WCAG 2.1 Level AA compliant
  • Minimum contrast ratio 4.5:1 for text

🎨 Customization

Custom Styles

/* Override menu background */
.widget-flyout-menu .dropdown-menu {
background-color:#your-color;
}
/* Custom hover effect */
.widget-flyout-menu .dropdown-item:hover {
background-color:#your-hover-color;
transform:translateX(5px);
}
/* Custom spacing */:root {
--flyout-spacing:5px;
}
/* Custom transition speed */:root {
--flyout-transition:0.3s;
}

Custom Icons

Works with any icon library:

// Bootstrap Icons{label: 'Home',icon: 'bi bi-house',url: '#'}// Font Awesome{label: 'Home',icon: 'fas fa-home',url: '#'}// Material Icons{label: 'Home',icon: 'material-icons',url: '#'}

Custom Click Handlers

{label: 'Export',icon: 'bi bi-download',onClick: function(item,event){event.preventDefault();// Your custom logicexportData();// Close menu$(event.target).closest('.dropdown-submenu').removeClass('show');}}

🔧 Advanced Usage

AJAX Loading

$.ajax({url: '/api/menu',method: 'GET',success: function(data){renderFlyoutFromJson('#menu',data);},error: function(){console.error('Failed to load menu');}});

Dynamic Menu Update

// Clear existing menu$('#menu').empty();// Render new menurenderFlyoutFromJson('#menu',newMenuData);

Multiple Menus

// Initialize multiple menus independentlyrenderFlyoutFromJson('#menu1',data1);renderFlyoutFromJson('#menu2',data2);renderFlyoutFromJson('#menu3',data3);

Event Handling

// Custom event after menu renders$(document).on('flyout:rendered','#menu',function(){console.log('Menu rendered!');});// Item click event$(document).on('click','.widget-flyout-menu .dropdown-item',function(e){constitemText=$(this).text();console.log('Clicked:',itemText);});

Pre-rendered HTML Menu

<divid="my-menu"><ulclass="widget-flyout-menu list-unstyled"><liclass="dropdown-submenu"><ahref="#" class="dropdown-toggle dropdown-item"><iclass="bi bi-house"></i>Home
</a><ulclass="dropdown-menu"><li><ahref="/dashboard" class="dropdown-item"><iclass="bi bi-speedometer2"></i>Dashboard
</a></li><liclass="dropdown-submenu"><ahref="#" class="dropdown-toggle dropdown-item"><iclass="bi bi-gear"></i>Settings
</a><ulclass="dropdown-menu"><li><ahref="/profile" class="dropdown-item"><iclass="bi bi-person"></i>Profile
</a></li></ul></li></ul></li></ul></div><script>initWidgetFlyout('#my-menu');</script>

🐛 Troubleshooting

Menus Not Opening

Problem: Submenus don't open when clicked/hovered.

Solutions:

// 1. Check if jQuery is loadedconsole.log(typeof$!=='undefined');// Should be true// 2. Check if library is loadedconsole.log(typeofwindow.initWidgetFlyout!=='undefined');// Should be true// 3. Ensure menu is initializedinitWidgetFlyout('#menu-container');// 4. Check console for errors// Open browser DevTools (F12) and check Console tab

Dark Mode Not Working

Problem: Theme doesn't change or persist.

Solutions:

// 1. Initialize themeFlyoutTheme.init();// 2. Check current themeconsole.log(FlyoutTheme.getTheme());// 3. Manually set themeFlyoutTheme.setTheme('dark');// 4. Check if localStorage is availableconsole.log(typeof(Storage)!=="undefined");// 5. Check data-bs-theme attributeconsole.log(document.documentElement.getAttribute('data-bs-theme'));

Submenus Hidden Behind Content

Problem: Dropdowns appear behind other elements.

Solutions:

/* Check parent containers for overflow:hidden */
.parent-container {
overflow: visible !important;
}
/* Increase z-index if needed */
.dropdown-menu {
z-index:10000!important;
}

RTL Not Working

Problem: Menu doesn't flip for RTL languages.

Solutions:

<!-- Ensure dir attribute is set --><divdir="rtl"><divid="menu"></div></div><!-- Or on html element --><htmldir="rtl">

Icons Not Visible

Problem: Icons don't show up.

Solutions:

<!-- 1. Ensure icon library is loaded --><linkhref="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet"><!-- 2. Check icon class names --><script>// Correct{label: 'Home',icon: 'bi bi-house',url: '#'}// Wrong - missing 'bi' prefix{label: 'Home',icon: 'house',url: '#'}</script>

Touch Devices Not Working

Problem: Menus don't work on mobile/tablets.

Solution: The library automatically detects touch devices. If not working:

// Force touch device mode$('body').addClass('touch-device');// Then reinitializeinitWidgetFlyout('#menu');

Flicker on Page Load (Dark Mode)

Problem: Brief flash of light theme before dark mode applies.

Solution: Add inline script in <head> before CSS:

<head><script>(function(){consttheme=localStorage.getItem('flyout-theme')||'light';document.documentElement.setAttribute('data-bs-theme',theme);})();</script><!-- Then your CSS files --></head>

🔧 Browser Support

Fully Supported

BrowserVersionNotes
Chrome90+Full support
Edge90+Full support
Firefox88+Full support
Safari14+Full support
Opera76+Full support
iOS Safari14+Touch optimized
Chrome Android90+Touch optimized

Legacy Browser Support

BrowserSupport Level
IE 11⚠️ Limited (no dark mode, basic functionality only)
Edge Legacy⚠️ Limited

Feature Detection

The library includes automatic feature detection for:

  • Touch devices
  • prefers-color-scheme support
  • localStorage availability
  • matchMedia API

📱 Examples

Example 1: Simple Menu

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><title>Simple Menu</title><linkhref="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"><linkhref="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet"><linkhref="css/widget-flyout.css" rel="stylesheet"></head><body><divclass="widget"><divclass="widget-header bg-primary text-white px-3 py-2"><span>Menu</span></div><divclass="widget-body p-3" id="menu"></div></div><scriptsrc="https://code.jquery.com/jquery-3.7.1.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script><scriptsrc="js/widget-flyout.js"></script><script>constdata=[{label: 'Home',icon: 'bi bi-house',url: '/'},{label: 'About',icon: 'bi bi-info-circle',url: '/about'},{label: 'Contact',icon: 'bi bi-envelope',url: '/contact'}];$(document).ready(function(){renderFlyoutFromJson('#menu',data);});</script></body></html>

Example 2: Menu with Dark Mode

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><title>Menu with Dark Mode</title><linkhref="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"><linkhref="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet"><linkhref="css/widget-flyout.css" rel="stylesheet"><style>body {
background-color:var(--bs-body-bg);
color:var(--bs-body-color);
transition: all 0.3s ease;
padding:2rem;
}
</style></head><body><buttonclass="btn btn-primary mb-3" id="toggleTheme">Toggle Dark Mode</button><divclass="widget"><divclass="widget-header bg-success text-white px-3 py-2"><span>Categories</span></div><divclass="widget-body p-3" id="menu"></div></div><scriptsrc="https://code.jquery.com/jquery-3.7.1.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script><scriptsrc="js/widget-flyout.js"></script><script>constdata=[{label: 'Electronics',icon: 'bi bi-laptop',children: [{label: 'Computers',icon: 'bi bi-pc-display',url: '/computers'},{label: 'Phones',icon: 'bi bi-phone',url: '/phones'}]},{label: 'Books',icon: 'bi bi-book',url: '/books'}];$(document).ready(function(){FlyoutTheme.init();renderFlyoutFromJson('#menu',data);$('#toggleTheme').on('click',function(){FlyoutTheme.toggle();});});</script></body></html>

Example 3: RTL Menu

<!DOCTYPE html><htmllang="fa" dir="rtl"><head><metacharset="UTF-8"><title>منوی فارسی</title><linkhref="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"><linkhref="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" rel="stylesheet"><linkhref="css/widget-flyout.css" rel="stylesheet"></head><bodystyle="padding: 2rem;"><divclass="widget"><divclass="widget-header bg-danger text-white px-3 py-2"><span>دسته‌ها</span></div><divclass="widget-body p-3" id="menu"></div></div><scriptsrc="https://code.jquery.com/jquery-3.7.1.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script><scriptsrc="js/widget-flyout.js"></script><script>constdata=[{label: 'خانه',icon: 'bi bi-house',children: [{label: 'داشبورد',icon: 'bi bi-speedometer2',url: '/dashboard'},{label: 'تنظیمات',icon: 'bi bi-gear',url: '/settings'}]},{label: 'درباره ما',icon: 'bi bi-info-circle',url: '/about'}];$(document).ready(function(){renderFlyoutFromJson('#menu',data);});</script></body></html>

🎨 Demo Files

Main Demo

Open index.html to see:

  • LTR menus (left sidebar)
  • RTL menus (right sidebar)
  • JSON and HTML initialization
  • Dark mode toggle
  • Keyboard navigation
  • Touch/hover behavior

Dark Mode Demo

Open example-dark-mode.html to see:

  • Light/Dark/Auto theme switching
  • Theme API demonstration
  • CSS variables showcase
  • Multiple menus in dark mode
  • localStorage persistence

📊 Performance

  • CSS file size: ~3 KB (minified)
  • JS file size: ~5 KB (minified)
  • Runtime overhead: <1ms
  • Theme switch time: <50ms
  • No layout recalculation on theme change (uses CSS variables)

🔒 Security

  • No external API calls
  • No user tracking
  • localStorage only for theme preference
  • No cookies used
  • XSS protection maintained
  • Safe error handling throughout

♿ Accessibility (WCAG 2.1 Level AA)

  • ✅ Minimum contrast ratio 4.5:1 for text
  • ✅ Minimum contrast ratio 3:1 for UI components
  • ✅ Focus indicators visible in all modes
  • ✅ Keyboard navigation fully supported
  • ✅ ARIA attributes on all interactive elements
  • ✅ Screen reader compatible
  • ✅ No information conveyed by color alone
  • ✅ Touch targets minimum 44x44px

📝 License

Provided as-is for use in your projects.


🆘 Support

Demo Files

  • index.html - Main demonstration
  • example-dark-mode.html - Dark mode examples

Quick Test

Paste this in browser console to test:

consttestData=[{label: 'Test 1',url: '#1'},{label: 'Test 2',url: '#2',children: [{label: 'Sub 1',url: '#s1'}]}];renderFlyoutFromJson('#menu',testData);FlyoutTheme.toggle();

Version: 2.0.0
Last Updated: November 2025
Status: ✅ Production Ready

About

A Bootstrap 5/jQuery FlyoutMenu

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages