Creating a keyboard and mouse accessible drop-down menu

I have a project for a client that necessitates a unique drop-down menu, which can be accessed by clicking or using the keyboard to navigate to it.

Currently, my solution is almost flawless, except for one issue: when you click the drop-down menu for the first time, it briefly opens and then closes again. I suspect there might be a small glitch in my JavaScript and jQuery code causing this behavior!

The desired functionality includes:

  1. Allowing the user to click and open a drop-down menu. (PARTIALLY ACHIEVED, but with glitches)
  2. Enabling the user to click on another menu, automatically closing the previous menu and opening the new one. (DONE)
  3. Allowing the user to click anywhere else on the page to close the open menu. (DONE)
  4. Facilitating tab navigation through the menu items, highlighting each link and opening the associated menu when tabbed to. (DONE)
  5. Ensuring that the tabbing sequence navigates through the entire menu before moving to the next link or menu, closing the previous one. (DONE)

Even though most of the functionality is working properly, the issue arises when the keyboard focus is triggered as soon as the mouse focus occurs, leading to the menu closing instantly after being opened.

I believe there might be a simple fix to this problem. Any assistance would be greatly appreciated!

JavaScript Code

$(document).ready(function() {
function toggle(select) {
    closeAll(select);
    if(select.hasClass("expanded")) {
        select.removeClass("expanded");
    } else {
        select.addClass("expanded");
    }
}
function closeAll(select) {
    var close = $(document).find("#navigation li.select.expanded");
    if(select != null) {
        close = close.not(select);
    }
    close.removeClass("expanded")
}

$("#navigation > ul > li.select > a").click(function(event) {
    event.preventDefault();
    toggle($(this).parent());
});
$(document).mouseup(function(event) {
    var select = $("#navigation > ul > li.select.expanded");
    if(!select.is(event.target) && select.has(event.target).length === 0) {
        closeAll();
    }
});
$(document).on("focus", "#navigation > ul > li > a", function () {
    closeAll();
    if($(this).parent().hasClass("select")) {
        toggle($(this).parent());
    }
});
});

HTML Code

    <div id="navigation">
        <ul>
            <li><a href="#"><i class="fa fa-home"></i> Home</a></li>
            <li class="select">
                <a href="#"><i class="fa fa-users"></i> Find a Society</a>
                <ul>
                    <li><a href="#">Scotland</a></li>
                    <li><a href="#">North West</a></li>
                    <li><a href="#">North East</a></li>
                    <li><a href="#">Midlands</a></li>
                    <li><a href="#">Eastern Counties</a></li>
                    <li><a href="#">Central Counties</a></li>
                    <li><a href="#">Wales</a></li>
                    <li><a href="#">South West Counties</a></li>
                    <li><a href="#">Southern Counties</a></li>
                    <li><a href="#">South East Counties</a></li>
                    <li><a href="#">Greater London</a></li>
                </ul>
            </li>
        </ul>
    </div>

Current Design Details

Answer №1

Recently, I encountered a similar situation where I had to implement a dropdown menu with advanced features like typeahead list filtering. In the process, I managed to resolve the issue.

The key is to establish only one entry point for the dropdown menu, with all other methods triggering the same event. In this scenario, the tab focus serves as the entry point, so you should initiate it on click:

UPDATE: Resolving Open/Close Issue Based on OP Feedback

$("#navigation > ul > li.select > a").click(function(event) {
    event.preventDefault();
    if($(this).hasClass('expanded')){
        closeAll()
    }
    else {
        $(this).focus(); #This is where the magic happens :)
    }
});

Now, the click event triggers a tab focus event that toggles the visibility of the dropdown menu.

To simplify closing the dropdown when clicking outside, you can utilize event delegation and the :not() selector in CSS. This approach may offer a slight performance improvement compared to your current implementation, though the complexity of the CSS selector may limit the speed gain.

$(document).on('mouseup',':not(#navigation > ul > li.select.expanded)',(function(event) {
    closeAll();
});

Furthermore, you can streamline your toggle function by leveraging jQuery's toggleClass method

function toggle(select) {
    closeAll(select);
    select.toggleClass('expanded');
}

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

One effective way to redirect after a PUT request and display a one-time message

Here's what I'm aiming for in terms of desired behaviour: A user navigates to a password change web page. The user completes the form and it is sent via PUT request to our REST server. Upon successful completion, the user is redirected to their ...

Is there a way I can relocate all the blocks to the top?

[I am seeking to elevate the phone icon + numbers block, social media icons block, and callback block to the level of the "tesla logo". I have attempted adjustments with padding and margin, but have been unsuccessful. 1 ...

Could converting a 47-byte JSON string into 340 MB be possible through JSON stringification?

var keys = [7925181,"68113227"]; var vals = {"7925181":["68113227"],"68113227":["7925181"]}; var temp = []; for (var i = 0; i < keys.length; i++) { temp[keys[i]] = vals[keys[i]]; } //alert(JSON.stringify(vals).length); alert(JSON.stringify(temp).le ...

Choose only one option from the dropdown menu at a time within the specified div

I attempted to utilize the "setSelected" option on my multiselect feature, but I noticed that it does not function with div elements (at least I could not make it work myself). I am endeavoring to create two synchronized multiselects using this example: b ...

Can a virtual host proxy utilize an external IP address?

Currently, I have three node apps running on the same server but with different localhost ports. My goal is to create a router that acts as a proxy for each app and then place this proxy in a virtual host. While I am currently testing this setup on my loca ...

Updating GridView row only if there are changes, the value remains unchanged in JavaScript. Disappointing

Incorporating a gridview (ASP.net) inside an update panel, I included a "Save" button that triggers a loop through all the rows of the grid view to pass data to a stored procedure for updating each row. However, this process proved to be slow and resulted ...

Trouble with displaying a cube from Blender to Three.js

Hey everyone, I'm having some trouble exporting a cube from Blender to three.js. I've exported the JSON file, but when I try to display the cube in my code, it's not showing up - instead, all I see is the setColor screen and I can't fig ...

Executing a snippet of Bootstrap 5 code under specific circumstances

I need to execute different code blocks based on the window width. When the width is 500px or more, I want to run a specific block of code, and when it's 499px or less, I need another block of code to run. How can I achieve this? For widths of 500px ...

What could be causing this JavaScript to output undefined?

const urls = [ "http://x.com", "http://y.com", "http://z.com", ]; for (let j=0; j<urls.length; j++) { setTimeout(function() { console.log(urls[j]); }, 3000); } I'm inserting this code snippe ...

Use the lodash chunk method to split a mapped array into two separate arrays

Looking to organize some data into tables? I have a dataset from an API that you might find helpful. [ { date: "1/11", data: [ { camera: "camera 1", count: 10 }, { camera: "camera 2", count: 20 ...

Tips for breaking out of an infinite loop while loading HTML containing a carousel into a blank <div> within another HTML file using jQuery's $(document).ready() function

Currently, I am studying HTML, CSS, Bootstrap 4, jQuery3, and JavaScript (ES6+), and to enhance my skills, I have devised an exercise for practice and consolidation. I have created 4 HTML files, all featuring responsive design based on Bootstrap 4: inde ...

Entering a new row and sending information through ajax

I'm looking for some help with a web page I have that includes a particular table structure: **Check out my Table*:* <table id="staff" class="table"> <thead> <tr> <th>First Name</th> <th>Last Nam ...

Organize array by year and month groupings

I'm trying to organize an array of events by year and month. Here is a sample of my data: const events = [ { name: "event 1", year: 2021, month: 1, }, { name: "event 2", year: 2021, month: 9, }, { ...

I am developing a quiz application using JavaScript, and I am wondering how I can smoothly transition from one question to the

I'm working on developing a quiz application and I'm facing an issue where my quiz is skipping question 2 when moving from one question to the next using the "next" button. I have a total of 3 questions in my quiz and for some reason, it jumps fr ...

The functionality of Express' render/redirect is limited to being triggered only by a submit method within a form

Objective To execute a POST request through a JavaScript method in order to send variable values as parameters. Setup NodeJS Express BodyParser ejs Initial Approach Frontend: <html> <head> <script src='http://ajax.go ...

Utilizing AngularJS to Extract JSON Data from Gzipped Files Stored Locally via Ajax

Currently, I am attempting to retrieve and read a JSON file that is stored in data.gz using AngularJS's $http service. However, when I make the request, all I get back are strange symbols and characters as the response. My application is being run loc ...

Installing v8-profiler on Windows 8 (64 bit) through npm: a step-by-step guide

The v8-profiler module is widely recognized as the go-to tool for identifying memory leaks in node.js applications. However, attempting to install it with npm install v8-profiler results in an error message related to compatibility issues between 32bit an ...

Press the button to switch classes and update the text using jQuery

Currently, I am developing content blocks that should toggle when a button is clicked. The functionality works as intended at the moment, but there is an issue with the text not changing on the button when it is focused or active and the content is toggled ...

having each individual div function on its own accord

Is there a more efficient way to make HTML and CSS function independently without duplicating the code multiple times and changing IDs or class names? Currently, only the top male and female button works when clicked. I am looking for a CSS-only solution t ...

What is the solution to preventing borders from appearing on input tags when they are autocompleted

Recently, I encountered an issue while working on a website form where the input tag was showing borders during autocompletion, despite specifying border:none. I tried various CSS pseudo classes to resolve this problem but none of them worked. Does anyon ...