Skip to content

bug: CRITICAL - span-based dropdown toggles completely non-functional (Drupal compatibility) #15

@jjroelofs

Description

@jjroelofs

Description

CRITICAL BUG: When Drupal renders menu items without an href, it outputs <span> tags instead of <a> tags for dropdown toggles. These span-based dropdowns do not work at all - clicking them does nothing and the dropdown never opens.

Example HTML (from Drupal)

<li class="nav-item dropdown" role="none">
  <span class="nav-link dropdown-toggle" id="productsLink" role="button" 
        data-bs-toggle="dropdown" aria-expanded="false">
    Products
  </span>
  <ul class="dropdown-menu">...</ul>
</li>

Symptoms

  1. Dropdown completely non-functional - Clicking the span-based toggle does absolutely nothing. The dropdown menu never opens.
  2. No pointer cursor - <span> elements don't get cursor: pointer by default, giving no visual indication that it's clickable.
  3. Missing keyboard accessibility - Span elements are not focusable without tabindex="0".

Root Cause Analysis

1. No click handler for top-level span toggles

In src/js/enhanced-dropdowns.js, the _attachToggleHandlers() method (lines 134-142) only attaches click handlers for submenus, not top-level dropdowns:

_attachToggleHandlers(toggleElement, dropdownInstance, isSubmenu = false) {
  this._attachKeyboardHandler(toggleElement, dropdownInstance);
  
  // Add click handler only for submenus (top-level uses Bootstrap's native handling)
  if (isSubmenu) {
    this._attachClickHandler(toggleElement, dropdownInstance);
  }
}

Top-level dropdowns rely on Bootstrap's native click handling via initPopperConfig() (lines 194-206):

initPopperConfig() {
  const existingToggles = document.querySelectorAll('[data-bs-toggle="dropdown"]');
  existingToggles.forEach(toggle => {
    if (bootstrap.Dropdown.getInstance(toggle)) return;
    if (toggle.closest('.bs-dropdown-wrapper, .bs-dropdown-item-wrapper')) return;
    const config = this._getPopperConfig(toggle);
    new bootstrap.Dropdown(toggle, config);  // Creates instance but no custom click handler
  });
}

Problem: Bootstrap's Dropdown component has issues with <span> elements - its internal event handling doesn't properly trigger for spans, even when the instance is created.

2. CSS doesn't apply cursor to spans

Bootstrap's CSS only sets cursor: pointer on a.dropdown-toggle and button.dropdown-toggle, not on generic .dropdown-toggle.

Proposed Fix

JavaScript fix (add click handler for span toggles):

initPopperConfig() {
  const existingToggles = document.querySelectorAll('[data-bs-toggle="dropdown"]');
  existingToggles.forEach(toggle => {
    if (bootstrap.Dropdown.getInstance(toggle)) return;
    if (toggle.closest('.bs-dropdown-wrapper, .bs-dropdown-item-wrapper')) return;
    const config = this._getPopperConfig(toggle);
    const dropdownInstance = new bootstrap.Dropdown(toggle, config);
    
    // Add click handler for non-anchor/non-button toggles (e.g., spans from Drupal)
    if (toggle.tagName !== 'A' && toggle.tagName !== 'BUTTON') {
      toggle.addEventListener('click', () => dropdownInstance.toggle());
    }
  });
}

CSS fix:

.nav-link.dropdown-toggle,
.dropdown-item.dropdown-toggle {
    cursor: pointer;
}

HTML requirement:

Ensure span toggles have tabindex="0" for keyboard accessibility.

Environment

  • Bootstrap 5.3
  • bs-enhanced-dropdowns 1.x
  • Drupal CMS rendering menus with span elements for items without URLs
  • Affects any system that renders dropdown toggles as spans instead of anchors

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions