Jquery: Navigating in menu using tab - javascript

I want to find a general rule that will help me navigate in menus, using tab. I know how to catch the tab event but there are several problems:
There are drop-down menus that i can't accese only be pressing tab key.
The structure of the drop-down menus is not standard. i.e. the hidden ul elements may be inside other elements (e.g div) or may be nested drop-down menus inside other drop-down menus
The events that make the hidden menus to be visible/invisible vary. Click and hover events are the most popular.
Can someone help me?

You can use the tabindex attribute on the list item that triggers the showing of your dropdown. This wil make the parent li focusable by pressing tab. You can then, with some CSS and Javascript, apply rules to children to make them visible.
About the fiddle:
Note that I use the opacity and pointer-events properties in CSS to switch states of the dropdown. When an element has visibility: hidden or display: none applied, it will not be focusable at all. Since the tab key by default brings focus to the next focusable element, your target element should not have either of those properties set to the shown values at the point the tab key is being pressed.
Also, you'll have to accurately keep track of the tabindexes throughout your navigation. First is the first main anchor, then the enclosing list-item, to make the dropdown visible. Then come the anchors inside that. (this is the point where JS has to take over from CSS) This brings us at tabindex 5 when the 3 subItems inside are assigned a tabindex, so we'll continue counting from 6 for the next main item's direct <a> child, and so forth.
You'll have to figure out the way to make your multi-level dropdowns work as expected yourself, but this is a starting point. You could additionally check in your script for arrow key presses, and give another element focus in response.
Fiddle:
$(function() {
$('nav').on('focus', '.dropdown a', function() {
$(this).closest('.mainItem').addClass('focus');
}).on('blur', '.dropdown a', function() {
$(this).closest('.mainItem').removeClass('focus');
});
});
nav > ul {
display: flex;
}
nav ul {
list-style: none;
margin: 0;
padding: 0;
}
nav .mainItem {
position: relative;
margin: 0 5px;
padding: 0.5rem;
background-color: #eee;
}
nav .dropdown {
position: absolute;
width: 100%;
left: 0;
top: 100%;
opacity: 0;
pointer-events: none;
}
nav .mainItem:hover .dropdown,
nav .mainItem:focus .dropdown,
nav .mainItem.focus .dropdown,
nav .mainItem > a:hover .dropdown,
nav .mainItem > a:focus .dropdown
nav .mainItem.focus .dropdown {
opacity: 1;
pointer-events: all;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<ul>
<li class="mainItem" tabindex="2">Parent 1
<ul class="dropdown">
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
<li class="mainItem" tabindex="7">Parent 2
<ul class="dropdown">
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
</ul>
</nav>

Related

sub-menu expansion and innerHTML replacement based on onclick, using just Javascript

First, I should mention that I'm about 4-weeks new to the coding world, and this is the first time I'm trying to make (what I thought would be) a simple site.
I have seen many similar questions on Stack Overflow, but in trying to adapt the code samples provided in the solutions, the solution would stop working.
So, the current hurdle is:
I have a menu defined in HTML with a sub-menu in one of the <li> elements ("Portfolio"), and that <li> element contains the character ▼ (&#9660).
I set up an onclick event for that <li> element so that when it was clicked it would do two things: expand/display the sub-menu <li> elements directly below it (pushing the other <li> elements in the menu further down), and replace the ▼ character with a ▲ character (&#9650)... until the <li> element was clicked again to shrink/hide the sub-menu.
I'm not sure if it matters, but this menu is inside a grid item because the page is set up using CSS Grid.
So basically:
HOME
ABOUT US
PORTFOLIO ▼
INFORMATION
CONTACT
...would become:
HOME
ABOUT US
PORTFOLIO ▲
LINK 1
LINK 2
LINK 3
INFORMATION
CONTACT
No matter how I set up my classes and IDs, I cannot get the arrow symbol to swap, and somewhere along the line, I messed up the coding and now the sub-menu doesn't even expand anymore.
It's likely embarrassingly bad code (given that I've tried to mash together bits from samples I've seen) but here is what I have. Thanks in advance.
var arrowstring = document.getElementById("arrowdirection").innerHTML;
document.getElementById("IDforPortfolioLink").classList.toggle("show");
if (IDforPortfolioLink.classList.contains('show')) {
arrowstring = "&#9650"
} else {
arrowstring = "&#9660"
}
arrowdirection.textContent = arrowstring;
}
.sub-menu-content {
display: none;
position: absolute;
}
.sub-menu-content a {
display: block;
}
.sub-menu-content a:hover {
background-color: green;
}
.show {
display: block;
}
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<ul class="menu">
<li>Home</li>
<li>About us</li>
<li class="LinkForPortfolio" id="IDforPortfolioLink" onclick= "myFunction()">LINK <span class="arrow" id= "arrowdirection">▼</span><div class="sub-menu-content" id="myportfolio">
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
</div>
</li>
<li>Information</li>
<li>Contact</li>
</ul>
</body>
</html>
Well, your code had so many flaws I had to rewrite it.
I'll explain everything that I possibly can of what I did here:
I changed the HTML a bit: I have added div's, instead of ul's with li's, inside a nav(container). It's more indicated to do so because it keeps the markup clean, and is less harder to debug.
I have assigned nav a display: flex; justify-content: center; align-items: center; which centers the divs inside nav, and inlines them. I did so with nav div, which pretty much centered the text inside of them.
I have removed all of the classes expect of .portfolio because it's useless to have that many classes.
I made div.expand-portfolio a child of div.portfolio, which in itself(.expand-portfolio) has another ul child, which holds the links. You might've noticed that I've added .portfolio a position: relative; and .expand-portfolio a position: absolute;. I did that because, I wanted to take .expand-portfolio out of the document flow, which basically means I wanted to make .expand-portfolio not interact with any element on the page. Now, when assigning position: absolute; to a child inside a container, the child's position is going to be relative to the document and not the parent. This is why you may add position: relative; to the parent.
I created a separate class called .expanded which gives .expand-portfolio a height of 150px when assigned to it.
You also might have noticed I gave the divs inside the nav a transition: 500ms ease, what that does is make the transition between the properties smooth, and not sudden. You may remove that property from them if you don't want that.
Now, the javascript.
When I made those 3 variables, which are the references of the elements from the page, you noticed I used document.getElementsByClassName followed by a [0]. What document.getElementsByClassName() returns is: a nodelist. Documentation here. It's basically a sort of "array", and with [0] appended to it, I select only the first and only element of the page with that class.
You may have observed I added the onclick function in the javascript file. Personal preference. I said that when I click the portfolio button, first, you should change that span's innerHTML. (the span element holds the actual symbol). I also said you should toggle the .expanded class. And, I made an if statement, checking if .expand-portfolio doesn't contain the class. If it doesn't, you can pretty much see what it does.
I hope it helps. If you have any more questions, ask them in the comments.
var portfolio = document.getElementsByClassName("portfolio")[0];
var portfolioInner = document.getElementsByClassName("inner-html")[0];
var expandPortfolio = document.getElementsByClassName("expand-portfolio")[0];
portfolio.onclick = function(){
portfolioInner.innerHTML = "▲";
expandPortfolio.classList.toggle("expanded");
if(!expandPortfolio.classList.contains("expanded")){
portfolioInner.innerHTML = "▼"
}
};
html, body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
.menus-container {
height: 100%;
width: auto;
}
.menus-container > div {
padding: 10px 10px 5px;
transition: 500ms ease;
width: 30%;
height: 100%;
cursor: pointer;
}
.expand-portfolio {
overflow: hidden;
width: 100px;
height: 0;
background-color: #000;
transition: 500ms ease;
}
.expand-portfolio ul {
padding-left: 25px;
}
.expand-portfolio ul li {
padding: 10px 0 10px 0;
color: #fff;
}
.portfolio span {
margin-left: 5px;
}
.expanded {
height: 150px;
}
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="menus-container">
<div>Home</div>
<div>About Us</div>
<div class="portfolio">Portfolio <span class="inner-html">&#9660</span>
<div class="expand-portfolio">
<ul>
<li>LINK 1</li>
<li>LINK 2</li>
<li>LINK 3</li>
</ul>
</div>
</div>
<div>Information</div>
<div>Contact</div>
</div>
</body>
</html>

Changing the direction in which an ul expands when a button is clicked

So I got a button which when clicked, toggles the visibility of an ul. My button element is a child of my footer, which is fixed to the bottom of my screen. The issue I am having is that when toggled, the ul expands downward instead of up. How would I go about making my ul expand upwards with the first li item starting from the bottom, with each succeeding li item stacking on top of the one before it.
Here is my code:
<footer class="web-stream">
<button type="button" class="active-stream">All</button>
<ul class="feed-list">
<li>feed 1</li>
<li>feed 2</li>
</ul>
</footer>
CSS
.web-stream {
background: green;
width: 100%
height: 2.3em;
margin: auto;
position: fixed;
bottom:0%;
left:0px;
right:0px;
}
.active-stream {
width: 9.5em;
height: 2.3em;
text-align: center;
}
.feed-list {
display: none;
}
Jquery:
jQuery(document).ready(function(){
jQuery(document).on('click','.active-stream', function(event) {
jQuery('.feed-list').toggle('fast'); //use 'slow'
});
});
Here's a JSFiddle.
If I understand your question properly, you just need to reverse the order of the list. If the content is dynamically generated then you could do this when you output the <li> tags. Otherwise the JS in this JSFiddle can accomplish the same thing.
I also moved the button to appear below the list to match your drawing.

Jquery - dropdown menu on click

I am relatively new to jquery and I am seeking help. The aim is to click on a list item attached to a ul and have it appear whilst any other list items disappear. Only the active one is viewable
The Issue I am having is that when I click on another list item the active one disappears (as intended), but it doesn't reveal the other one, it remains hidden. I am looking for a way to reveal the list, while hiding the ones that are in-active.
I have uploaded my problem: http://jsfiddle.net/CbU4d/
html:
<div id="secondary-nav"><!--secondary-nav-->
<ul>
<li>Current Article
<ul>
<li>Example 1</li>
</ul>
</li>
<li class="active">Past Articles
<ul>
<li>Example 1</li>
<li>Example 2</li>
<li>Example 3</li>
</ul>
</li>
</ul>
</div><!--/secondary-nav-->
css:
#secondary-nav {
float:left;
height:auto;
width:23%; /*210px*/
border-right:2px solid #000;
position:relative;
}
/*heading styles*/
#secondary-nav ul li {
padding: 0 10px;
list-style-type: none;
}
#secondary-nav ul li a {
font-family:TrajanPro;
font-size:1em;
line-height: 32px;
color:#000;
}
/*links*/
#secondary-nav ul ul li a {
display: block;
font-family:TrajanPro;
font-size:0.9em;
line-height: 27px;
text-decoration: none;
color:#000;
transition: all 0.15s;
}
#secondary-nav ul li a:hover {
display:block;
color:#af2931;
text-decoration:underline;
}
#secondary-nav ul ul {
display: none;
}
#secondary-nav li.active ul {
display: block;
}
/css
jquery using 1.7.1
$(document).ready(function(){
$("#secondary-nav ul").click(function(){
//slide up all the link lists
$("#secondary-nav ul ul").slideUp();
//slide down the link list below the h3 clicked - only if its closed
if(!$(this).next().is(":visible"))
{
$(this).next().slideDown();
}
})
})
Try with
$("#secondary-nav ul ul").slideToggle();
Demo
I got it to work (I think) by making two changes:
Change the selector on line 2 to this:
"#secondary-nav ul li"
This means the event will be attached to the list item you click, not the entire list.
Remove the if statement on line 6. Since we're hiding all of the second level uls in the previous line, we don't need to check if it's visible; we know it isn't.
Change line 6 to this:
$(this).children('ul').slideDown();
This is because the ul you want to unfold is a child of the li you're clicking, not a sibling.
Here's my fixed jsFiddle.
Edit: If you want to stop it from hiding and re-showing when you click the one that's already expanded, just chuck this at the top of the handler:
if ($(this).children('ul').is(':visible')){
return
}

Keep submenu while moving mouse

I want to make a menu (with submenu and div containing link for every submenu on hover).
Something like this:
AAAA | BBBB
| bbb1
| bbb2 HERE IS MOUSE (bbb2 LINK)
| bbb3
It is possible to keep listing submenus when you try to open link? If you move mouse from "bbb2" link disappear.
what I have now:
http://i.imgur.com/vhFtaQc.png
and what I want:
http://i.imgur.com/BOQNMat.png
Here is JSFiddle: http://jsfiddle.net/zu8Eu/
Hope you understand. Thanks!
As is noted you may need to nested the <div> inside the li elements. But additional to keep the hover() you can use padding to set the white space:
HTML
<li id="submenu1">
aaaa1
<div class="one">
Link for aaa1
</div>
</li>
CSS
.one {
display: none;
position: absolute;
top: 0px;
left:100%;
}
The demo http://jsfiddle.net/zu8Eu/27/
I'm sure the are jQuery plugins that will provide you with what you want to achieve, but I'd say restructure you HTML and nest the elements properly.
<ul>
<li>Menu 1
<ul>
<li>
Submenu 1
<ul>
<li>
Subsub menu
</li>
</ul>
</li>
</ul>
</li>
</ul>
This way, you can use CSS to achieve what you want:
ul li ul{
display: none;
}
ul li:hover > ul{
display: block;
}
ul li ul li:hover > ul{
display: block;
}
Once you hover on the first li, it will show the direct child ul, if you hover on that ul, it will still count as a hover on the first li, meaning it'll still be visible.
Fiddle: http://jsfiddle.net/x54gZ/

CSS single select list example

I am looking for a click based single select list. Instead of the normal drop down list, I want an overlay menu (similar to how we see on facebook when we change privacy settings, the single select list that appears). Is that CSS based or any code examples to creating a similar list? All the lists i found of the net are hover lists not same as we see on f/b.
Thanks.
Something like this:
It is just a menu that pops up where use can pick an option.
If you were using jQuery it would be something like this plug-in
http://plugins.jquery.com/project/selectbox
I can't find a non JS-solution (and for JS I'm using jQuery to assign focus to an arbitrary element that would keep the sub-menu open (on the plus side, I'm pretty darn sure that Facebook will be using JavaScript, of some sort, to achieve their implementation too).
However, I've put together a couple of examples, these are, much like Facebook, simply dropdown lists, with a JavaScript handler to assign focus to the clicked element (to keep the sub-menu open regardless of mouse position on the page, until the user clicks elsewhere).
With that in mind, I've used the standard css dropdown-style mark-up:
<ul>
<li>Symbol
<ul>
<li>option 1</li>
<li>option 2</li>
<li>option 3</li>
</ul>
</li>
<li>Symbol
<ul>
<li>option 1</li>
<li>option 2</li>
<li>option 3</li>
</ul>
</li>
<li>Symbol
<ul>
<li>option 1</li>
<li>option 2</li>
<li>option 3</li>
</ul>
</li>
</ul>
With the css:
ul {
list-style-type: none;
padding: 0;
margin: 0 auto;
}
ul li {
display: inline-block;
position: relative;
width: 100px;
border-bottom: 1px solid #ccc;
}
ul li ul {
display: none;
width: 100%;
position: absolute;
top: 1em;
left: 0;
}
ul li a:hover + ul,
ul li a:active + ul,
ul li a:focus + ul {
display: block;
}
ul li ul li {
border: 0 none transparent;
}
And the jQuery:
$(document).ready(
function() {
$('li a').click(
function(){
$(this).focus();
});
}
);
Demo at JS Bin
Notice that I've used a elements, since focus is more easily assigned to anchors than plain, in this case, li elements. However a second version, with the same end result is achieved with the following jQuery:
$(document).ready(
function() {
$('li').click(
function(){
$('li').not(this).removeAttr('tabindex');
$(this).attr('tabindex','-1').focus();
});
}
);
Demo at JS Bin.
Addenda
Stu Nicholls, of CSS Play seems to have achieved a pure-css solution for this (with a gallery), but I haven't worked my way through his CSS to see how he did it.

Categories

Resources