I'm working on an application that needs to be fully keyboard controllable, everything is working great up until now, but i can't make the JQuery UI Menu widget works when using the ENTER key.
From the documentation, there seems to be no specifics method to make this work so i'm using "onclick" on the list elements
<li onclick="alert('Books Action')"><div>Books</div></li>
Here is a jsfiddle (https://jsfiddle.net/djbh6r82/) with the example from JQuery UI documentation and an alert on the first menu item -> It works fine when i click on it but with 'ENTER' it just bring me back to the start (focus on menu is not lost)
$(function() {
$("#menu").menu();
});
.ui-menu {
width: 150px;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
<ul id="menu">
<li class="ui-state-disabled">
<div>Toys (n/a)</div>
</li>
<li onclick="alert('Books Action')">
<div>Books</div>
</li>
<li>
<div>Clothing</div>
</li>
<li>
<div>Electronics</div>
<ul>
<li class="ui-state-disabled">
<div>Home Entertainment</div>
</li>
<li>
<div>Car Hifi</div>
</li>
<li>
<div>Utilities</div>
</li>
</ul>
</li>
<li>
<div>Movies</div>
</li>
<li>
<div>Music</div>
<ul>
<li>
<div>Rock</div>
<ul>
<li>
<div>Alternative</div>
</li>
<li>
<div>Classic</div>
</li>
</ul>
</li>
<li>
<div>Jazz</div>
<ul>
<li>
<div>Freejazz</div>
</li>
<li>
<div>Big Band</div>
</li>
<li>
<div>Modern</div>
</li>
</ul>
</li>
<li>
<div>Pop</div>
</li>
</ul>
</li>
<li class="ui-state-disabled">
<div>Specials (n/a)</div>
</li>
</ul>
Am I doing this wrong ? I tried different thing ( instead of div, onclick on div instead of ul) but nothing seems to work
When you review the API Docs, you will see under https://api.jqueryui.com/menu/ the following details:
Keyboard interaction
ENTER/SPACE: Invoke the focused menu item's action, which may be opening a submenu.
UP: Move focus to the previous menu item.
DOWN: Move focus to the next menu item.
RIGHT: Open the submenu, if available.
LEFT: Close the current submenu and move focus to the parent menu item. If not in a submenu, do nothing.
ESCAPE: Close the current submenu and move focus to the parent menu item. If not in a submenu, do nothing.
Typing a letter moves focus to the first item whose title starts with that character. Repeating the same character cycles through matching items. Typing more characters within the one second timer matches those characters.
Disabled items can receive keyboard focus, but do not allow any other interaction.
So the Enter is already engaged and will trigger the select event for the Menu. You will then want to perform some action in the select callback.
$(function() {
$("#menu").menu({
select: function(e, ui) {
console.log(ui.item.text().trim(), "Selected");
}
}).focus();
});
.ui-menu {
width: 150px;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
<ul id="menu">
<li class="ui-state-disabled">
<div>Toys (n/a)</div>
</li>
<li>
<div>Books</div>
</li>
<li>
<div>Clothing</div>
</li>
<li>
<div>Electronics</div>
<ul>
<li class="ui-state-disabled">
<div>Home Entertainment</div>
</li>
<li>
<div>Car Hifi</div>
</li>
<li>
<div>Utilities</div>
</li>
</ul>
</li>
<li>
<div>Movies</div>
</li>
<li>
<div>Music</div>
<ul>
<li>
<div>Rock</div>
<ul>
<li>
<div>Alternative</div>
</li>
<li>
<div>Classic</div>
</li>
</ul>
</li>
<li>
<div>Jazz</div>
<ul>
<li>
<div>Freejazz</div>
</li>
<li>
<div>Big Band</div>
</li>
<li>
<div>Modern</div>
</li>
</ul>
</li>
<li>
<div>Pop</div>
</li>
</ul>
</li>
<li class="ui-state-disabled">
<div>Specials (n/a)</div>
</li>
</ul>
Giving it focus up front allows it to already be invoked.
Related
I a working on a to make a reponsive dropdown navigation bar with vanilla JavaScript. In mobile view I want that upon click one dropdown the another should close. JavaScript here:
dropbtns.forEach(link => {
link.addEventListener("click", function(e) {
e.currentTarget.nextElementSibling.classList.toggle("showdd");
});
});
and show dropdown:
.showdd {
height: auto;
}
html code:
<div class="nav-container">
<div class="brand">
Logo
</div>
<nav>
<div class="nav-mobile"><a id="nav-toggle" href="#!"><span></span></a></div>
<ul id="nav-list">
<li>
Home
</li>
<li>
About
</li>
<li class="dropdown">
</i>
<ul class="nav-dropdown">
<li>
Web Design
</li>
<li>
Web Development
</li>
<li>
Graphic Design
</li>
</ul>
</li>
<li>
Pricing
</li>
<li class="dropdown">
</i>
<ul class="nav-dropdown">
<li>
Web Design
</li>
<li>
Web Development
</li>
<li>
Graphic Design
</li>
</ul>
</li>
<li>
Contact
</li>
</ul>
</nav>
</div>
full code can be find here.
So, if you want to collapse all other .nav-dropdown when one is being clicked on, you simply will need to:
Store the reference of the .nav-dropdown of the current element (for comparison later)
Toggle its class (as you're doing already)
Go through all other .nav-dropdown in your DOM tree and iterate through them. If they do not match the current reference, then you know the dropdown belongs to another link and you can remove the class
With that in mind we arrive at the code below:
dropbtns.forEach(link => {
link.addEventListener('click', e => {
const ownDropdown = e.currentTarget.nextElementSibling;
ownDropdown.classList.toggle('showdd');
document.querySelectorAll('.dropbtn + .nav-dropdown').forEach(el => {
if (el !== ownDropdown)
el.classList.remove('showdd');
});
});
});
It works on your Codepen after I edit the following line.
links.forEach(link => {
link.addEventListener("click", function(e) {
links.forEach(link => {
link.nextElementSibling.classList.remove("showdd"); // Here
});
e.currentTarget.nextElementSibling.classList.toggle("show");
});
});
By the way, what is "showdd"?
I'm trying to be able to toggle these sub menus one at a time, I'm getting lost in nests and cant quite figure out how to target the correct list item,
I found that I should be using find() instead of children() as it can go deeper in the nest but still no luck in getting it working.
<ul>
<li>Profile</li>
<li>Edit</li>
<li class="drop-nav"> See your products
<ul>
<li class="drop-nav"> Mens
<ul>
<li> jumpers </li>
<li> t shirts </li>
</ul>
</li>
<li class="drop-nav"> Womens
<ul>
<li> hoodies </li>
<li> leggings </li>
</ul>
</li>
</ul>
</li>
</ul>
$(".drop-nav").on("click", function(e){
e.preventDefault();
});
li ul{
display: none;
}
You could use $(this).find('ul').eq(0) to get the ul, but I would delegate the changing of the display to the stylesheet, but use javascript to add a class where applicable. This will give you many more options for the design of your dropdown later.
$(".drop-nav").on("click", function(e) {
e.preventDefault()
// don't allow the event to fire horizontally or vertically up the tree
e.stopImmediatePropagation()
// switch the active class that you can use to display the child
$(this).toggleClass('active')
})
/* don't target ll list items in you page, be more specific */
.drop-nav > ul {
display: none;
}
.drop-nav.active > ul {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>Profile</li>
<li>Edit</li>
<li class="drop-nav"> See your products
<ul>
<li class="drop-nav"> Mens
<ul>
<li> jumpers </li>
<li> t shirts </li>
</ul>
</li>
<li class="drop-nav"> Womens
<ul>
<li> hoodies </li>
<li> leggings </li>
</ul>
</li>
</ul>
</li>
</ul>
I would add more descriptive class names in your markup, and make them easier to target with CSS and jQuery.
To toggle the menus you could do something like the following:
$(".dropdown-trigger1").on("click", function() {
// Toggle the first menu
$(".dropdown-one").toggleClass("open");
// Close the submenus
$(".dropdown-two").removeClass("open");
});
$(".dropdown-trigger2").on("click", function(e) {
// Prevent a click on a submenu from closing the menu
e.stopPropagation();
// Close any open submenu
$(".dropdown-two").removeClass("open");
// Open the submenu that has been clicked
$(this).find(".dropdown-two").toggleClass("open");
});
li ul {
display: none;
}
.dropdown-one.open {
display: block;
}
.dropdown-two.open {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>Profile</li>
<li>Edit</li>
<li class="dropdown-trigger1"> See your products
<ul class="dropdown-one">
<li class="dropdown-trigger2"> Mens
<ul class="dropdown-two">
<li> jumpers </li>
<li> t shirts </li>
</ul>
</li>
<li class="dropdown-trigger2"> Womens
<ul class="dropdown-two">
<li> hoodies </li>
<li> leggings </li>
</ul>
</li>
</ul>
</li>
</ul>
You haven't described about how you activate each sub-menu, so I will describe solution little bit abstractly. Solution is based on your HTML structure an will work if you wouldn't change it.
$('.drop-nav a').on('click', function() {
// This next method returns next element in DOM that is after clicked a link.
// Based on your HTML it would be ul that holds your sub-menu.
var subMenu = $(this).next();
// Here using subMenu selector to make something with sub-menu...
// Example: adding CSS inline to sub. In your situation it may be something else...
$(subMenu).css({ 'display' : 'block' });
});
I'm trying to change the colour of active menu/submenu which is selected by user.
For example when Sub11 is clicked, Link 1 and Sub11 will both turn red. Likewise when Sub22 is clicked, Link 2 and Sub22 will be red.
ul a { cursor: pointer; }
.active { color:red;}
<ul>
<li class="active"><a>Link 1</a>
<ul>
<li><a>Sub11</a></li>
<li><a>Sub12</a></li>
</ul>
</li>
<li><a>Link 2</a>
<ul>
<li><a>Sub21</a></li>
<li><a>Sub22</a></li>
</ul>
</li>
<li><a>Link 3</a></li>
</ul>
try using jquery this code:
$('.link > a').click(function(){
$('.active').removeClass('active');
$(this).addClass('active');
});
$('.submenu > li').click(function(){
$('.active').removeClass('active');
$(this).addClass('active');
$(this).parent('ul').prev('a').addClass('active');
});
Explanation:
The first part: whenever you click on .link item you remove every other active item and set the clicked one to active.
The second part: whenever you click on a submenu item you remove active state from the previous item and set it to the clicked submenu item and its predecessor .link item
You gonna have to add classes link and submenu to your html like this:
<ul>
<li class="link">
<a class="active">Link 1</a>
<ul class="submenu">
<li><a>Sub11</a></li>
<li><a>Sub12</a></li>
</ul>
</li>
<li class="link">
<a>Link 2</a>
<ul class="submenu">
<li><a>Sub21</a></li>
<li><a>Sub22</a></li>
</ul>
</li>
<li class="link"><a>Link 3</a></li>
</ul>
Here is a fiddle
Basically i want to click on a tab and a drop down menu appears then when you re-click the same tab or any of the others I want it to hide that tab/show the other tab if clicked on the same/other tab.
I tried
$('.click').click(function() {
$(this).find('.sub-nav-list').toggleClass('active');
});
and tried
$('.click').click(function() {
$('.sub-nav-list').removeClass('active');
$(this).find('.sub-nav-list').toggleClass('active');
});
but cant work it out! any insight? Thanks
html:
<nav class="secondary-nav">
<ul class="list clearfix">
<li class="leaders click">Leadership <span class="arrow">></span>
<ul class="sub-nav-list">
<li>Management</li>
<li>Board of Directors</li>
</ul>
</li>
<li class="contact click">Contact Info <span class="arrow">></span>
<ul class="sub-nav-list">
<li>Email Notification</li>
<li>Information Request</li>
</ul>
</li>
<li class="docs click">Documents <span class="arrow">></span>
<ul class="sub-nav-list">
<li>Governance Documents</li>
<li>Press Release</li>
<li>Reports & Presentations</li>
<li>Sec Filings</li>
<li>Frenquently Asked Questions</li>
<li>Tax Information</li>
</ul>
</li>
<li class="research click">Research <span class="arrow">></span>
<ul class="sub-nav-list">
<li>Dividends and Distributions</li>
<li>Stock Information</li>
<li>Analyst Coverage</li>
<li>Market Makers</li>
</ul>
</li>
</ul>
</nav>
I can see at least two possible issues there.
1) sub-nav-list is not a children of click element. If they are on the same level something like that might work:
$('.click').click(function() {
$(this).parent().find('.sub-nav-list').toggleClass('active');
});
2) You have these elements generated dynamically - so you need use on with selector of any parent element that exists before you dynamically generate your sub-menus (let say nav-list):
$(".click").on("click", ".nav-list", function() {
$(this).parent().find('.sub-nav-list').toggleClass('active');
});
I´m doing a menu that every item has it´s submenu. this is the markup:
<ul id="menu">
<li>
TITLE 1
<ul class="submenu">
<li>
sub1
</li>
<li>
sub2
</li>
</ul>
</li>
<li>
TITLE 2
<ul class="submenu">
<li>
sub1
</li>
<li>
sub2
</li>
</ul>
</li>
<li>
TITLE 3
</li>
<li>
TITLE 4
</li>
</ul>
css
.submenu{display:none}
script
function show_submenu(that) {
$('ul.submenu').slideUp();
$(that).next('ul.submenu:first').slideDown();
}
The problem I have is that if I click over an item, it slides down it´s submenu, BUT if I click over it again, it slidesup and slidesdown again it´s submenu, which is already opened... kind of weird effect for the user... any ideas how to fix it?
You need to check whether the existing submenu is visible already. If it is, you don't want to slide it down again.
var $submenu = $(that).next('ul.submenu:first'),
viz = $submenu.is(":visible");
$('ul.submenu').slideUp();
if (!viz) {
$(that).next('ul.submenu:first').slideDown();
}
http://jsfiddle.net/ExplosionPIlls/zep6H/
use toggle in jquery
$('#foo').toggle(showOrHide);
http://api.jquery.com/toggle/
It's doing exactly what you coded it to do. Instead, use .slideToggle();
function show_submenu(that){
$(that).next('ul.submenu:first').slideToggle();
}
jsFiddle example
this should help
function show_submenu(that){
if(!$(that).next('ul.submenu:first').is(":visible")){
$('ul.submenu').slideUp();
$(that).next('ul.submenu:first').slideDown();
}
}