Navigating the dropdown sub menus in Bootstrap using keyboard shortcuts

Enabling WCAG compliance for a dropdown menu requires keyboard navigability, in addition to mouse functionality. It seems that the bootstrap developers removed sub-menu support some time ago due to mobile unfriendliness, but my specific needs are limited to desktop use.

I attempted to incorporate code from this answer for keyboard navigation, but was unsuccessful in navigating through the submenu.

Upon updating to bs4.5 library, key navigation works perfectly on the top-level dropdown and even reaches the first element in the submenu. However, pressing the up and down keys within the submenu only hides the popup.

Even after preventing the menu from hiding with a keydown event listener, I am still unsure how to navigate within the submenu as expected utilizing the active class attribute.

My requirements include:

  • Navigating the submenu using UP/DOWN keys
  • 'Clicking' submenu elements with the enter key

This is the appearance after activating the submenu with the enter key.

https://i.sstatic.net/CIm1i.png

Here is what happens when moving to the submenu by pressing the down arrow.

https://i.sstatic.net/EZqrz.png

The code (mostly unchanged from the linked question, excluding sub-sub menus):

<html>
    <head>
        <style>
            .dropdown-submenu {
                position: relative;
            }

            .dropdown-submenu a::after {
                transform: rotate(-90deg);
                position: absolute;
                right: 6px;
                top: .8em;
            }

            .dropdown-submenu .dropdown-menu {
                top: 0;
                left: 100%;
                margin-left: .1rem;
                margin-right: .1rem;
            }
        </style>
    </head>
        
    <!-- Bootstrap, jQuery, Popper.js -->
    
</html>

How can I successfully navigate within the submenu?

Answer №1

Through analyzing the bootstrap dropdown plugin's source code, I managed to successfully navigate the focus caret within the sub menu by implementing the following code snippet.

By incorporating a keydown event handler to the submenus dropdown list, we can extract the list of available items as native elements and identify the current index by locating the target of the key event. Subsequently, we can adjust the index accordingly (either increment or decrement) and set the focus on the new item.

$('.dropdown-submenu .dropdown-menu').on('keydown', function(e) {            

    const items = $(this).find('.dropdown-item').get();
    let index = items.indexOf(event.target)

    if (e.which == 38) {
        index--;
        event.preventDefault()
        event.stopPropagation()
    } else 
    if (e.which == 40) {
        index++;
        event.preventDefault()
        event.stopPropagation()
    }

    if(index < 0 || index > (items.length - 1)) {
        return
    }

    items[index].focus()
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to Retrieve the jqXHR Object with jQuery Deferreds and Ajax

In the jQuery Deferreds documentation, there is an example illustrating how to return ajax arguments including jqXHR: $.when( $.ajax("test.php") ).then(function(ajaxArgs){ alert(ajaxArgs[1]); /* ajaxArgs is [ "success", statusText, jqXHR ] */ }); Ho ...

Steps to select an option from a drop-down menu that does not have an ID assigned

How can I interact with a drop-down element that appears after clicking on an item? <div class="field loan-selection"> <label class="field__body"> <div class="field__label">Nettokreditbetrag <!-- -->&nbs ...

React is unable to identify the property that was passed to a styled-component in Material UI

Custom Styled Component Using Material-UI: import { Typography } from '@material-ui/core'; const CustomText = styled(Typography)<TextProps>` margin-bottom: 10px; color: ${({ textColor }) => textColor ?? textColor}; font-size: ${( ...

What location is most ideal for incorporating JavaScript/Ajax within a document?

Sorry for the seemingly simple question, but I would appreciate some clarification from the experts. When it comes to the three options of placing JavaScript - head, $(document).ready, or body, which would be the optimal location for ajax that heavily rel ...

What is the best way to use Ajax to return a Blade view in a controller?

I am looking to show a view using ajax in the controller no matter what the response is. To achieve this, refresh the page after the process. ...

Difficulty encountered while implementing the if-else statement in raycasting operations

Currently, I am experimenting with raycasting to determine if my mouse is pointing at an object. The function seems to be working fine when the object is not being touched - it correctly prints out "didnt touch". However, when the object is touched, it s ...

Using the hash(#) symbol in Vue 3 for routing

Even though I am using createWebHistory, my URL still contains a hash symbol like localhost/#/projects. Am I overlooking something in my code? How can I get rid of the # symbol? router const routes: Array<RouteRecordRaw> = [ { path: " ...

javascriptJQuery validation function triggers JSF ajax request

I am currently experiencing a challenge with JSF ajax requests and jQuery events. I am using a jQuery event to validate a section of a form like this: jQuery(".btnNextStep").live("click", function(e) { var error = !validate(id); ... ...

bootstrap class not detected and not visible in Firebug

I have been delving into a book about bootstrap in order to master it. Upon downloading the bootstrap files and integrating them into my test file, test.html, I encountered an issue where the "navbar-toggle" class was not recognized by my browser and did n ...

Getting stuck trying to fetch the queryset from the server and adding it to the select options using AJAX

I have managed to successfully send the value of the input field with the id datepicker to the server. In the view, I am able to filter the time slots based on the selected date. However, I am facing a challenge in sending this queryset back to the browse ...

Oops! You're trying to perform actions that must be plain objects. If you need to handle async actions

I have been struggling to implement Redux and pass an object into the store. Although I am able to fetch the correct object when I call the action, the store remains unchanged when I use store.dispatch(). It still only reflects the initial state. I've ...

Karma is reporting an error with TypeScript, saying it cannot locate the variable 'exports'

Currently, I am in the process of mastering how to write Unit Test cases for an angular project coded in Typescript. To facilitate this, I have opted for utilizing Karma and Mocha. Below lays out the structure of the application: Project/ ├── app/ ...

Adjusting Screen Size for Lottie Animation in React.js

I need assistance with resizing a lottie animation component on my website's landing page to set its display to none on mobile devices. How can I achieve this? See the lottie component code below: import React from "react"; import Lottie fr ...

Identifying the presence of an image in a directory and displaying a standard image if not found

I have a directory containing pictures of different wines, each labeled with a specific code (e.g. FMP-HTR17). I would like to show the corresponding picture if it is available, but display a default image if the specific picture does not exist in the dire ...

Balanced Height Panels - Utilizing Flexbox and flex-wrap: wrap

i'm having trouble with a layout design, specifically with getting the columns to be equal height, especially when the text wraps around. Can anyone provide some guidance? https://i.stack.imgur.com/bbDKj.png Within my HTML template, there are multipl ...

Implement the callback-console.log feature from the epic-games-api into an Express.js application

Looking to integrate Epic Games output into an Express.js GET request but don't have any JavaScript experience, so go easy on me! XD const EpicGamesAPI = require('epicgames-status'); const express = require('express') const app = ...

How can I apply a class to a list item when clicked using Vue.js and a template component generated by v-for loop?

I'm struggling to add an active class to a list item in a template component when it's clicked, making sure that only one item can have the class at a time. I've attempted different solutions such as passing a new data object on click and r ...

Locate the indices of several maximum values within an array

Here is an array for reference: var exampleArray = [10, 67, 100, 100]; The goal is to identify the indexes of the maximum values in the array. The existing function currently locates only one index: function findMaxIndexes(arr) { var max = arr[0] ...

Guide on populating a dropdown menu with values based on the selection of another dropdown menu

Could you please help with a situation involving two dropdown lists, one for country and one for state? I am trying to load states based on the chosen country value. I have set up an AJAX call and triggered the change event for the country dropdown. My m ...

Error: Mapping values to cards resulted in a TypeError: Property 'map' cannot be read from undefined source

After following a tutorial from this website, I attempted to map my post details to cards. However, the error "TypeError: Cannot read property 'map' of undefined" popped up. Despite searching on various platforms like Stack Overflow for a soluti ...