Contact Us
We'd love to hear from you!
/*prevent FOUC on mobile when using sidebar */ .dwc-mobile :is(.bricks-is-frontend.brx-header-left, .bricks-is-frontend.brx-header-right) #brx-header { position: relative; inline-size: 100%; flex-direction: column; } .dwc-mobile .bricks-is-frontend:is(.brx-header-left, .brx-header-right) :is(#brx-content, #brx-footer) { margin-inline-start: 0; } /*prevent FOUC on desktop when using sidebar */ .bricks-is-frontend:is(.brx-header-left, .brx-header-right):not(.show-nav) #dwc_nav_wrapper { display: none; } /*=== sidebar css ===*/ html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header { flex-direction: column; box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.7); } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]).no-scroll { overflow: visible; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items { max-block-size: 100dvb; padding-block-end: 12rem; overscroll-behavior: contain; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #dwc_nav_wrapper { position: relative; overflow: hidden; block-size: 100%; transform: translateX(0%); visibility: visible; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #dwc-nest-menu .brxe-toggle { display: none !important; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu { display: flex; flex-direction: column; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-header > div { display: flex; grid-template-columns: 1fr; block-size: 100%; flex-direction: column; justify-content: flex-start; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-menu-wrap { inline-size: 100%; display: grid; grid-template-columns: 1fr; block-size: 100%; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header :is(.brxe-code, .dwc-nest-menu-overlay) { display: none; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-header { inline-size: 100%; padding-inline: 0; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #mobile_menu_top { min-block-size: var(--top-offset); } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #dwc-nest-menu:not( [data-hide-close-bar = 'true']) .brx-dropdown-content { inset-block-start: calc(var(--top-offset) + 1px) !important; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #dwc-nest-menu:not( [data-submenu-reveal = 'slide']) .brx-dropdown-content:not([data-submenu-reveal = 'slide'] *) { inset-inline-start: 0; inset-block-start: unset !important; overflow: hidden; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #dwc-nest-menu:not([data-submenu-reveal = 'slide']) .brxe-dropdown:not(.open.active) > .brx-dropdown-content > .brxe-dropdown:not([data-submenu-reveal = 'slide'] *){ visibility: hidden; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested.brx-open .brxe-dropdown > .brx-dropdown-content { overscroll-behavior: contain; min-inline-size: var(--mobile-menu-width); } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #dwc-nest-menu .brxe-dropdown.open > .brx-submenu-toggle button:not([data-submenu-reveal = 'expand'] button) { min-block-size: calc(var(--top-offset) - 1px); inset-block-start: 0; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #dwc-nest-menu .brxe-dropdown.open[data-submenu-reveal = 'slide'] > .brx-submenu-toggle button { min-block-size: calc(var(--top-offset) - 1px); inset-block-start: 0; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header #dwc-nest-menu .brxe-dropdown .brx-submenu-toggle button { min-block-size: 0; } /* sidebar css ends*/ /*sidebar in builder*/ /*=== sidebar css ===*/ :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header { flex-direction: column; box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.7); --top-offset: 40px; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items { max-block-size: 100dvb; padding-block-end: 12rem; overscroll-behavior: contain; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header #dwc_nav_wrapper { position: relative; overflow: hidden; block-size: 100%; transform: translateX(0%); visibility: visible; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #dwc-nest-menu .brxe-toggle { display: none !important; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu { display: flex; flex-direction: column; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-header > div { display: flex; grid-template-columns: 1fr; block-size: 100%; flex-direction: column; justify-content: flex-start; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-menu-wrap { inline-size: 100%; display: grid; grid-template-columns: 1fr; block-size: 100%; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-header { inline-size: 100%; padding-inline: 0; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header #mobile_menu_top { min-block-size: var(--top-offset); } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header #dwc-nest-menu .brx-dropdown-content { inset-block-start: calc(var(--top-offset) - 1px) !important; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested.brx-open .brxe-dropdown > .brx-dropdown-content { overscroll-behavior: contain; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header #dwc-nest-menu .brxe-dropdown.open > .brx-submenu-toggle button { min-block-size: calc(var(--top-offset) - 1px); inset-block-start: 0; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header #dwc-nest-menu .brxe-dropdown .brx-submenu-toggle button { min-block-size: 0; } :is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu { margin: 0 !important; } :is(.brx-header-left, .brx-header-right)[data-builder-window] .brx-nav-nested-items { flex-direction: column !important; } :is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-toggle--open.brxe-toggle { display: flex !important; } :is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu .brx-nav-nested-items { position: relative !Important; background: var(--mobile-menu-bg) !important; align-items: stretch; flex: 1; } /* MENU CTA (LAST BUTTON) */ :is(.brx-header-left, .brx-header-right)[data-builder-window] [data-last-item-is-button="true"].dwc-nest-menu .brx-nav-nested-items > .menu-item:last-of-type { padding-inline: var(--menu-item-inline-padding) !important; padding-block: var(--menu-item-block-padding) !important; } :is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu-top { min-block-size: 80px !important; } :is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-nav-items { overflow-y: scroll; } :is(.brx-header-left, .brx-header-right)[data-builder-mode] .brx-dropdown-content { min-inline-size: var(--mobile-menu-width); position: static; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items { flex-wrap: nowrap; } :is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header #dwc-nest-menu .brx-dropdown-content { visibility: visible !important; opacity: 1; } /*OVERLAY SIDEBAR*/ html:not(.dwc-mobile):has([data-overlay-sidebar=true]) { --mobile-menu-bg: rgb(255 255 255 / 0%); --menu-item-border: solid 1px rgb(255 255 255 / 50%); } html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) :is(main, footer){ margin: 0 !important } html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) :is(main, footer) :where(section):not(section>section) { padding-inline-start: calc(var(--mobile-menu-width) + clamp(1.5rem, calc(0.625vw + 1.375rem), 1.875rem)); max-inline-size: 100% } html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) #brx-header { border-radius: var(--overlay-sidebar-radius); overflow: hidden; background: var(--overlay-sidebar-bg); box-shadow: var(--overlay-sidebar-shadow) !important; inset: var(--overlay-sidebar-inset); } html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) .dwc-nest-header{ backdrop-filter: blur(13px); background: transparent !important; } html:not(.dwc-mobile):has([data-overlay-sidebar=true]):not([data-builder-modee]) .brx-dropdown-content { background-color: rgb(255 255 255 / 100%); } /*NO BRX-OPEN STYLES*/ html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items { display: flex; flex-wrap: nowrap; flex-direction: column !important; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu.brxe-nav-nested .brxe-dropdown .brx-dropdown-content { visibility: visible; min-inline-size: var(--mobile-menu-width) !important; } html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu[data-submenu-reveal="expand"] .brxe-dropdown.open>.brx-dropdown-content { position: static; }
class SidebarNavigation { constructor(options = {}) { // Basic configuration properties this.config = { minWidth: options.minWidth || MegaMenuCONFIG.minWidth, // Using external minWidth variable menuSelector: options.menuSelector || '.dwc-nest-menu', openClass: options.openClass || 'brx-open', activeClasses: options.activeClasses || ['open', 'active'], leftHeaderClass: options.leftHeaderClass || 'brx-header-left', rightHeaderClass: options.rightHeaderClass || 'brx-header-right', debounceDelay: options.debounceDelay || 100, menuItemClickDelay: options.menuItemClickDelay || 300 }; // Set dependent selectors const menuSelector = this.config.menuSelector; this.config.submenuToggleSelector = options.submenuToggleSelector || `${menuSelector} .brx-submenu-toggle`; this.config.dropdownSelector = options.dropdownSelector || `${menuSelector} .brxe-dropdown`; this.config.dropdownContentSelector = options.dropdownContentSelector || `${menuSelector} .brx-dropdown-content`; // State this.previousHeaderClass = null; this.dropdownClickHandlers = new Map(); this.menuHoverHandlers = null; this.menuItemClickTimeout = null; this.keyboardNavHandler = null; this.cachedFocusableElements = null; this.cachedElements = { menuElement: null, navElement: null, dropdowns: null, dropdownToggles: null, menuItems: null }; // Bind methods to this instance this.handleResize = this.debounce(this.handleMenu.bind(this), this.config.debounceDelay); this.handleOutsideClick = this.handleOutsideClick.bind(this); } // Initialize everything - called once init() { // Wait for DOM to be fully loaded before attaching events if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { this.initAfterDOMLoaded(); }, { once: true }); } else { this.initAfterDOMLoaded(); } return this; } // Separate initialization method to run after DOM is loaded initAfterDOMLoaded() { // Cache DOM elements once this.cacheElements(); // Setup resize event with passive flag window.addEventListener('resize', this.handleResize, { passive: true }); // Setup mutation observer for critical class changes only this.setupMutationObserver(); // Initial setup based on current screen size this.handleMenu(); // Cache focusable elements once if header class is present if (this.hasHeaderClass()) { this.cacheFocusableElements(); this.setupMenuFocusNavigation(); } } // Cache all required DOM elements upfront cacheElements() { this.cachedElements.menuElement = document.querySelector(this.config.menuSelector); if (this.cachedElements.menuElement) { this.cachedElements.navElement = this.cachedElements.menuElement.querySelector('.dwc-nest-nav-items'); this.cachedElements.dropdowns = Array.from(document.querySelectorAll(this.config.dropdownSelector)); this.cachedElements.dropdownToggles = Array.from(document.querySelectorAll(this.config.submenuToggleSelector)); this.cachedElements.menuItems = Array.from(document.querySelectorAll(`${this.config.menuSelector} .menu-item`)); } } // Set up a focused mutation observer only for dropdown state changes setupMutationObserver() { if (!this.cachedElements.dropdowns || this.cachedElements.dropdowns.length === 0) return; const callback = (mutations) => { for (let mutation of mutations) { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { const target = mutation.target; const prevClassList = mutation.oldValue ? mutation.oldValue.split(' ') : []; const hadBothBefore = prevClassList.includes('open') && prevClassList.includes('active'); const hasBothNow = target.classList.contains('open') && target.classList.contains('active'); if (hadBothBefore !== hasBothNow) { this.updateDropdownAccessibility(); break; // Only need to update once per batch } } } }; // Create observer with optimized options this.classObserver = new MutationObserver(callback); // Observe only the dropdown elements this.cachedElements.dropdowns.forEach(dropdown => { this.classObserver.observe(dropdown, { attributes: true, attributeFilter: ['class'], attributeOldValue: true }); }); } // Cache focusable elements for keyboard navigation cacheFocusableElements() { if (!this.cachedElements.navElement) return; // Get direct children of nav const directChildren = Array.from(this.cachedElements.navElement.children); // Find the first focusable element within each direct child this.cachedFocusableElements = directChildren.map(child => { // Check if the child itself is focusable if (child.matches('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])')) { return child; } // Otherwise, find the first focusable element within this child return child.querySelector('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])'); }).filter(Boolean); // Remove null/undefined values } // Clean up all event listeners and observers destroy() { // Clean up the mutation observer if (this.classObserver) { this.classObserver.disconnect(); this.classObserver = null; } // Clean up resize listener window.removeEventListener('resize', this.handleResize); // Clean up click handlers if (this.dropdownClickHandlers.size > 0) { this.dropdownClickHandlers.forEach((handler, toggle) => { toggle.removeEventListener('click', handler); }); this.dropdownClickHandlers.clear(); } // Clean up hover handlers this.cleanupMenuHover(); // Clean up menu item click handlers this.cleanupMenuItemClicks(); // Clean up outside click handler document.removeEventListener('click', this.handleOutsideClick); // Clean up keyboard navigation if (this.keyboardNavHandler) { document.removeEventListener('keydown', this.keyboardNavHandler); this.keyboardNavHandler = null; } // Clear any pending timeouts if (this.menuItemClickTimeout) { clearTimeout(this.menuItemClickTimeout); this.menuItemClickTimeout = null; } } // Utility methods hasHeaderClass() { return document.body.classList.contains(this.config.leftHeaderClass) || document.body.classList.contains(this.config.rightHeaderClass); } debounce(func, delay) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => func(...args), delay); }; } // Check if an element has all the required active classes hasAllActiveClasses(element) { return this.config.activeClasses.every(className => element.classList.contains(className)); } // Toggle all active classes on an element toggleActiveClasses(element) { this.config.activeClasses.forEach(className => { element.classList.toggle(className); }); } // Core functionality methods handleMenu() { if (!this.cachedElements.menuElement) return; if (!this.hasHeaderClass() && !this.previousHeaderClass) return; const screenWidth = window.innerWidth; const isLargeScreen = screenWidth >= this.config.minWidth; const menuElement = this.cachedElements.menuElement; if (!isLargeScreen) { // Save which class was present before removal if (this.hasHeaderClass()) { this.previousHeaderClass = document.body.classList.contains(this.config.leftHeaderClass) ? this.config.leftHeaderClass : this.config.rightHeaderClass; // Remove header classes document.body.classList.remove(this.config.leftHeaderClass, this.config.rightHeaderClass); menuElement.classList.remove(this.config.openClass); // Reset accessibility attributes this.resetAccessibilityAttributes(); } // Clean up event handlers for mobile view this.cleanupMenuHover(); this.cleanupMenuItemClicks(); this.cleanupDropdownHandlers(); document.removeEventListener('click', this.handleOutsideClick); return; } // Large screen behavior if (!this.hasHeaderClass() && this.previousHeaderClass) { document.body.classList.add(this.previousHeaderClass); } if (this.hasHeaderClass()) { if (!menuElement.classList.contains(this.config.openClass)) { menuElement.classList.add(this.config.openClass); } // Setup elements for large screen view this.setupMenuHover(); this.setupMenuItemClicks(); this.setupDropdownHandlers(); this.setupMenuFocusNavigation(); this.updateDropdownAccessibility(); // Ensure outside click handler is set up document.removeEventListener('click', this.handleOutsideClick); document.addEventListener('click', this.handleOutsideClick, { passive: false }); } } // Reset accessibility attributes when switching to mobile resetAccessibilityAttributes() { if (!this.cachedElements.dropdowns) return; // Remove all inert attributes from dropdown contents this.cachedElements.dropdowns.forEach(dropdown => { const content = dropdown.querySelector(this.config.dropdownContentSelector); if (content) { content.removeAttribute('inert'); } const button = dropdown.querySelector('button'); if (button) { button.setAttribute('aria-expanded', 'false'); } }); } setupMenuFocusNavigation() { // Only run if hasHeaderClass() is true and we have focusable elements if (!this.hasHeaderClass() || !this.cachedFocusableElements || this.cachedFocusableElements.length === 0) { return; } // Clean up previous handler if it exists if (this.keyboardNavHandler) { document.removeEventListener('keydown', this.keyboardNavHandler, true); this.keyboardNavHandler = null; } const navMenu = this.cachedElements.menuElement; const focusableElements = this.cachedFocusableElements; const firstFocusableElement = focusableElements[0]; const lastFocusableElement = focusableElements[focusableElements.length - 1]; // Find adjacent focusable elements outside the menu (only once during setup) const headerElement = navMenu.closest('header') || document.querySelector('header'); // Prepare variables to hold adjacent elements let prevFocusableElement = null; let nextFocusableElement = null; let firstElementAfterHeader = null; if (headerElement) { // Get all focusable elements within the header - do this once and cache the result const headerFocusables = Array.from( headerElement.querySelectorAll('a:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])') ).filter(el => window.getComputedStyle(el).display !== 'none'); // Find the index of our first and last menu elements in one pass const menuStartIndex = headerFocusables.indexOf(firstFocusableElement); const menuEndIndex = headerFocusables.indexOf(lastFocusableElement); // Cache the adjacent elements if (menuStartIndex > 0) { prevFocusableElement = headerFocusables[menuStartIndex - 1]; } if (menuEndIndex !== -1 && menuEndIndex < headerFocusables.length - 1) { nextFocusableElement = headerFocusables[menuEndIndex + 1]; } // Pre-calculate the first element after header - but only if needed if (!nextFocusableElement) { // Use a more efficient selector that targets immediate children of body that aren't the header const selector = 'body > *:not(header)'; const nonHeaderElements = document.querySelectorAll(selector); // Only process if we have elements if (nonHeaderElements.length > 0) { // Create a function to find the first focusable element (used later if needed) this.findFirstFocusableAfterHeader = () => { for (const element of nonHeaderElements) { const focusable = element.querySelector('a:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])'); if (focusable && window.getComputedStyle(focusable).display !== 'none') { return focusable; } } return null; }; } } } // Create keyboard navigation handler with closure over the cached elements this.keyboardNavHandler = (e) => { // Quick check for Tab key first if (e.key !== 'Tab') return; // Then check if focus is inside the menu if (!navMenu.contains(document.activeElement)) return; let targetElement = null; // Handle tab navigation at boundaries only if (!e.shiftKey && document.activeElement === lastFocusableElement) { // Forward tab from last element e.preventDefault(); e.stopPropagation(); if (nextFocusableElement) { targetElement = nextFocusableElement; } else if (this.findFirstFocusableAfterHeader) { // Only search for elements after header if needed and not already found firstElementAfterHeader = this.findFirstFocusableAfterHeader(); targetElement = firstElementAfterHeader; } // Focus on the target or body as fallback setTimeout(() => { if (targetElement) { targetElement.focus(); } else { document.body.setAttribute('tabindex', '-1'); document.body.focus(); document.body.removeAttribute('tabindex'); } }, 10); } else if (e.shiftKey && document.activeElement === firstFocusableElement) { // Backward tab from first element e.preventDefault(); e.stopPropagation(); setTimeout(() => { if (prevFocusableElement) { prevFocusableElement.focus(); } else { document.body.setAttribute('tabindex', '-1'); document.body.focus(); document.body.removeAttribute('tabindex'); } }, 10); } }; // Use capture phase for the event document.addEventListener('keydown', this.keyboardNavHandler, true); } setupMenuHover() { const menuElement = this.cachedElements.menuElement; if (!menuElement) return; // Clean up existing hover handlers first this.cleanupMenuHover(); // Create event handlers const mouseenterHandler = () => { menuElement.classList.add(this.config.openClass); }; const mouseleaveHandler = () => { menuElement.classList.remove(this.config.openClass); }; // Add event listeners with passive flag for better performance menuElement.addEventListener('mouseenter', mouseenterHandler, { passive: true }); menuElement.addEventListener('mouseleave', mouseleaveHandler, { passive: true }); // Store the handlers for cleanup this.menuHoverHandlers = { element: menuElement, mouseenter: mouseenterHandler, mouseleave: mouseleaveHandler }; } cleanupMenuHover() { if (this.menuHoverHandlers) { const { element, mouseenter, mouseleave } = this.menuHoverHandlers; element.removeEventListener('mouseenter', mouseenter); element.removeEventListener('mouseleave', mouseleave); this.menuHoverHandlers = null; } } setupMenuItemClicks() { if (!this.cachedElements.menuItems || this.cachedElements.menuItems.length === 0) return; // Clean up existing handlers first this.cleanupMenuItemClicks(); const menuElement = this.cachedElements.menuElement; const menuItemHandlers = new Map(); this.cachedElements.menuItems.forEach(item => { const clickHandler = () => { if (this.hasHeaderClass()) { // Clear any existing timeout if (this.menuItemClickTimeout) { clearTimeout(this.menuItemClickTimeout); } // Set timeout before adding the class this.menuItemClickTimeout = setTimeout(() => { if (!menuElement.classList.contains(this.config.openClass)) { menuElement.classList.add(this.config.openClass); } }, this.config.menuItemClickDelay); } }; menuItemHandlers.set(item, clickHandler); item.addEventListener('click', clickHandler); }); this.menuItemClickHandlers = menuItemHandlers; } cleanupMenuItemClicks() { if (this.menuItemClickHandlers && this.menuItemClickHandlers instanceof Map) { this.menuItemClickHandlers.forEach((handler, item) => { item.removeEventListener('click', handler); }); this.menuItemClickHandlers.clear(); } if (this.menuItemClickTimeout) { clearTimeout(this.menuItemClickTimeout); this.menuItemClickTimeout = null; } } setupDropdownHandlers() { if (!this.hasHeaderClass() || !this.cachedElements.dropdownToggles) return; // Clean up existing handlers first this.cleanupDropdownHandlers(); this.cachedElements.dropdownToggles.forEach(toggle => { const clickHandler = (event) => { event.stopPropagation(); event.preventDefault(); const dropdown = toggle.closest(this.config.dropdownSelector); if (dropdown) { this.toggleActiveClasses(dropdown); this.updateDropdownAccessibility(); } }; this.dropdownClickHandlers.set(toggle, clickHandler); toggle.addEventListener('click', clickHandler); }); } cleanupDropdownHandlers() { if (this.dropdownClickHandlers.size > 0) { this.dropdownClickHandlers.forEach((handler, toggle) => { toggle.removeEventListener('click', handler); }); this.dropdownClickHandlers.clear(); } } handleOutsideClick(event) { if (event.target.tagName === 'A') return; if (!event.target.closest(this.config.dropdownSelector)) return; if (!event.target.closest(this.config.submenuToggleSelector)) { event.preventDefault(); event.stopPropagation(); } } updateDropdownAccessibility() { // Only run if hasHeaderClass() is true if (!this.hasHeaderClass() || !this.cachedElements.dropdowns) return; this.cachedElements.dropdowns.forEach(dropdown => { const content = dropdown.querySelector(this.config.dropdownContentSelector); const button = dropdown.querySelector('button'); // Check if dropdown has all active classes const isOpen = this.hasAllActiveClasses(dropdown); if (content) { if (isOpen) { content.removeAttribute('inert'); } else { content.setAttribute('inert', ''); } } if (button) { button.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); } }); } } const sidebarNav = new SidebarNavigation().init();