Change the focus to the following "li" item while ignoring any nested elements

I am working with a menu structure as shown below.

$('html').removeClass('no-js');
// Add plus mark to li that have a sub menu
$('li:has("ul") > a').append('<span class="plusMark">+</span>');

Mousetrap.bind('down', function(e) {

  if ($(".manu-t").children("ul").children("li").children(":focus").length != 0) {
    //mentioned below What I have tried till now
  }

});
#nav,
#nav ul,
#nav li {
  margin: 0;
  padding: 0;
  border: 0;
  list-style: none;
  box-sizing: border-box;
}

#nav {
  position: relative;
  min-height: 30px;
  max-width: 100%;
  background-color: #b5b5b5;
  color: #000;
}

#nav li {
  position: relative;
}

#nav a {
  text-decoration: none;
  height: 100%;
  display: block;
  padding: 0 20px;
}

#nav>ul,
.fa {
  height: 30px;
  line-height: 30px;
}

#nav>ul>li {
  position: relative;
  text-align: center;
}

#nav>ul>li>a {
  background-color: #b5b5b5;
}

#nav>ul>li>a:hover,
#nav>ul>li>a:focus,
#nav>ul>li>a.js-openSubMenu {
  background-color: #5f5f5f;
}

#nav>ul>li:hover>a,
#nav>ul>li:focus>a {
  background-color: #5f5f5f;
  color: #fff;
}

#nav>ul>li>ul>li>a {
  background-color: #5f5f5f;
}

#nav>ul>li>ul>li>a:hover,
#nav>ul>li>ul>li>a:focus {
  background-color: #b5b5b5;
}

#nav>ul>li>ul>li:not(:last-child) a {
  border-bottom: 1px solid #b5b5b5;
}

#nav>ul>li>ul>li>ul>li>a {
  background-color: #b5b5b5;
}

#nav>ul>li>ul>li>ul>li>a:hover,
#nav>ul>li>ul>li>ul>li>a:focus {
  background-color: #5f5f5f;
}

#nav>ul>li>ul>li>ul>li:not(:last-child)>a {
  border-bottom: 1px solid #5f5f5f;
}


/* Javascript classes */

#nav .js-hideElement {
  display: none;
}

#nav .js-showElement {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="containe-header">
  <nav id="nav">
    <ul>
      <li class="menu-t"><a href="#">Main</a>
        <ul>
          <li><a href="#">Item1</a>
            <ul>
              <li><a href="#">Sub Item 1</a></li>
              <li><a href="#">Sub Item 2</a></li>
              <li><a href="#">Sub Item 3</a></li>
              <li><a href="#">Sub Item 4</a></li>
            </ul>
          </li>
          <li><a href="#">Item2</a>
            <ul>
              <li><a href="#">Sub Item 1</a></li>
              <li><a href="#">Sub Item 2</a></li>
              <li><a href="#">Sub Item 3</a></li>
              <li><a href="#">Sub Item 4</a></li>
              <li><a href="#">Sub Item 5</a></li>
              <li><a href="#">Sub Item 6</a></li>
            </ul>
          </li>
          <li><a href="#">Item3</a></li>
          <li><a href="#">Item4</a></li>
        </ul>
      </li>
      <li class="menu-l"><a href="#">Menu2</a></li>
    </ul>
  </nav>
</div>

The challenge is to shift focus from Item1 to Item2 and then to Item3 by pressing the down arrow key (using Mousetrap library for keypress detection)

This is what I have attempted so far...

1.

var focused_index = 1;
focused_index=focused_index - 1;
$('a').eq(focused_index).focus();

However, this method focuses on nested elements, moving from Item1 to Sub Item 1, then Sub Item 2, and so on...

2.

// Get the focused element:
var $focused = $(':focus');

// No jQuery:
var focused = document.activeElement;

// Does the element have focus:
var hasFocus = $('foo').is(':focus');

// No jQuery:
elem === elem.ownerDocument.activeElement;

Nevertheless, this causes issues with other elements in the Body tag that have focus.

  1. Check out the answer to this question Send focus to dynamic li with jQuery

How can I successfully shift focus from Item1 to Item2?

Answer №1

I am interested in focusing on the element of Item2 and then moving to the element of Item3.

You can achieve this using the following code snippet:

$("a:focus").closest("li").next().find("a").first().focus();

Here is a working example:

// Adding a plus mark to li elements that have a submenu
$('li:has("ul") > a').append('<span class="plusMark">+</span>');

// Setting initial focus
$("#nav>ul>li.menu-t>ul>li>a:first()").focus();

$(document).on("keydown", function(e) {

  if (e.keyCode == 40) {
     $("a:focus").closest("li").next().find("a").first().focus();
     return false;
  } 
  if (e.keyCode == 38) {
     $("a:focus").closest("li").prev().find("a").first().focus();
     return false;
  }
  
});
a { text-decoration: none; }
:focus { background-color: yellow; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="containe-header">
  <nav id="nav">
    <ul>
      <li class="menu-t"><a href="#">Main</a>
        <ul>
          <li><a href="#">Item1</a>
            <ul>
              <li><a href="#">Sub Item 1</a></li>
              <li><a href="#">Sub Item 2</a></li>
              <li><a href="#">Sub Item 3</a></li>
              <li><a href="#">Sub Item 4</a></li>
            </ul>
          </li>
          <li><a href="#">Item2</a>
            <ul>
              <li><a href="#">Sub Item 1</a></li>
              <li><a href="#">Sub Item 2</a></li>
              <li><a href="#">Sub Item 3</a></li>
              <li><a href="#">Sub Item 4</a></li>
              <li><a href="#">Sub Item 5</a></li>
              <li><a href="#">Sub Item 6</a></li>
            </ul>
          </li>
          <li><a href="#">Item3</a></li>
          <li><a href="#">Item4</a></li>
        </ul>
      </li>
      <li class="menu-l"><a href="#">Menu2</a></li>
    </ul>
  </nav>
</div>

Answer №2

To focus on specific elements, you can utilize a CSS selector to gather them into an array. By adding an event handler for 'keydown' events and checking if the user pressed the down arrow key using e.code, you can cycle through the elements in the array. A generator function is used here to loop back to index 0 once the end of the array is reached:

function* nextElGenerator() {
  var liEls = document.querySelectorAll('.menu-t > ul > li > a:first-child');

  var i = 0;

  while (true) {
    yield liEls[i];
    i += 1;

    if (i === liEls.length) {
      i = 0;
    }
  }
}

const bulletElItr = nextElGenerator();
let curEl;

document.addEventListener('keydown', e => {
  if (e.code === 'ArrowDown') {
    if (curEl) {
      curEl.style.backgroundColor = 'transparent';
    }
    curEl = bulletElItr.next().value;
    curEl.style.backgroundColor = 'yellow';
  }
});
<div class="containe-header">
  <nav id="nav">
    <ul>
      <li class="menu-t"><a href="#">Main</a>
        <ul>
          <li><a href="#">Item1</a>
            <ul>
              <li><a href="#">Sub Item 1</a></li>
              <li><a href="#">Sub Item 2</a></li>
              <li><a href="#">Sub Item 3</a></li>
              <li><a href="#">Sub Item 4</a></li>
            </ul>
          </li>
          <li><a href="#">Item2</a>
            <ul>
              <li><a href="#">Sub Item 1</a></li>
              <li><a href="#">Sub Item 2</a></li>
              <li><a href="#">Sub Item 3</a></li>
              <li><a href="#">Sub Item 4</a></li>
              <li><a href="#">Sub Item 5</a></li>
              <li><a href="#">Sub Item 6</a></li>
            </ul>
          </li>
          <li><a href="#">Item3</a></li>
          <li><a href="#">Item4</a></li>
        </ul>
      </li>
      <li class="menu-l"><a href="#">Menu2</a></li>
    </ul>
  </nav>
</div>

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

Mobile Drag and Drop with JavaScript

While experimenting with a user interface I created, I utilized jQuery UI's draggable, droppable, and sortable features. However, I observed that the drag and drop functionality does not work in mobile browsers. It seems like events are triggered diff ...

problem with nivo slider causing content to overflow

My webpage is using nivoslider to display images. However, I am facing an issue where the images are overflowing one by one until the page fully loads. I have tried setting the overflow:hidden property in the CSS but it doesn't seem to correct the pro ...

Struggling to understand the concept of utilizing Promises for data retrieval

I'm currently facing an issue with my async function that awaits a GraphQL call. Even though the call returns a Promise containing the desired data, I'm struggling to access it effectively. Below is the snippet of code in question: export async ...

Pedaling back and forth along a sequence

Is there a way to implement forward and backward buttons for a clickable list without using arrays, as the list will be expanding over time? I have already achieved changing color of the listed items to red, but need a solution to navigate through the list ...

Issue with jQuery where it returns "undefined" when trying to access the CSS display property

On my page, there are several divs with default display styles set in an external .css file. When a certain condition is met, I use jQuery to turn one on and the others off using the fadeIn() and fadeOut() methods. However, I've noticed that in Firef ...

Tips for setting HTML content in a text field using SOAP API in Tuleap

We are using the Tracker SOAP API to create fresh tracker artifacts. While making changes to an artifact in Tuleap, there is an option to choose between plain text and html in a dropdown menu for a text field. Our goal is to input html-formatted text into ...

What causes the console.log function to behave in this manner?

When using the node.js interpreter, if you run the code: console.log("A newline character is written like \"\\ n \"."); //output will be:- // A newline character is written like "\ n ". However, if you just enter the following in ...

What is the best way to add prefixes to my SCSS style sheets?

After attempting to add prefixes to my scss files, I came across the autoprefixer tool. However, I discovered that it only supports CSS files. Is there a way to utilize autoprefixer with scss files? Here are the commands for Autoprefixer: npm install post ...

What could be causing the disappearance of the border around this `<li>` element?

I am currently redesigning the Kotaku website and facing difficulties in creating a list for the footer. Below is an image illustrating my issue. The 'Popular tags' section has been created using the <ul> and <li> elements, with an & ...

Create a div element that is set to be 30 pixels smaller than its containing div

I am trying to achieve a specific layout where I have a nested div structure. The parent div has a width set in percentage (80%) and I want the child div to be slightly narrower by 30px. Can anyone provide guidance on how I can accomplish this? Do I need ...

Generate a JavaScript variable in real-time and leverage it to retrieve the corresponding JSON response

After receiving a JSON response from an API as depicted in the image below: https://i.sstatic.net/5eq43.png I encountered an issue where the same column_code is used in both the variable_meta section and the data part of the response. This column code act ...

CKEditor4 plugins do not function properly when used with multiple instances within Vue Components

After developing a Vue component for rich text editing with CKEditor 4 on my website, I encountered an issue. When the component is first mounted, everything works perfectly including all default plugins. However, if another instance of the same component ...

Creating an object property conditionally in a single line: A quick guide

Is there a more efficient way to conditionally create a property on an object without having to repeat the process for every different property? I want to ensure that the property does not exist at all if it has no value, rather than just being null. Thi ...

What's the best way to manage endless routing options in express.js?

After reviewing the topic of handling routes in Express.js on Stack Overflow, I came across the following example : var express = require("express"); var app = express(); app.get("/", function(request, response) { response.send(&quo ...

Utilizing External Library Functions with VueJS

I have been looking everywhere for a working example of how to connect an external JS file and call its function, but I haven't had any luck. Essentially, my goal is to link an external JavaScript file and execute its function using VueJS. <script ...

Traverse JSON data with JavaScript/jQuery

I am looking to iterate through the JSON data provided below using JavaScript. { "jsonUrl": "/testUrl", "data": { "country":"US", "company":"ABC", "items":[ { "id": "1", "i ...

Hiding content with aria-hidden="true" results in the div

I am trying to create a dynamic slideshow where two specific elements are hidden using [aria-hidden=true]. I want the container of the slideshow to adjust its size based on these hidden elements. Is there anyone familiar with scripting this functionality ...

What is the method for adding up elements based on their identification numbers?

Is it possible to calculate the sum of multiple range sliders using their unique IDs? Multiplying each range slider value by the corresponding input. And finally, adding up all the multiplication results. $(document).ready(function() { $(".range") ...

Issues have been reported with Safari mobile on iOS version 10.2.1 where CSS links are not being detected

I am in the process of creating a website using HTML, PHP, JavaScript, and CSS. All three languages are included in the same document with a link to an external CSS file. As I test for browser compatibility, I discovered that Safari on iOS 10.2.1 is unable ...

Angular 4's Mddialog experiencing intermittent display problem

While using MDDialog in my Angular app, I've encountered a couple of issues. Whenever a user clicks on the div, flickering occurs. Additionally, if the user then clicks on one of the buttons, the afterclose event is not triggered. Can anyone provide ...