Can you set css min-width relative to another element? - javascript

I have a nav menu of links and sub menu. I want the submenu min width to be that of the link. Is this possible in CSS? I'm using LESS if that helps at all.
<ul>
<li>
Item FooBarBaz
<ul class="submenu">
<li><a>sub1</a></li>
<li><a>sub2</a></li>
<li><a>sub3</a></li>
</ul>
</li>
<li>
Item FooBarBazZipBamBop
<ul class="submenu">
<li><a>sub1</a></li>
<li><a>sub2</a></li>
<li><a>sub3</a></li>
</ul>
</li>
</ul>
I want each ul.submenu to have the min-width of the previous sibling anchor. Obviously this would be a potentially different value for each submenu. Is this possible in CSS? Or is jquery, javascript a better solution here?

Sure. Here is an example: http://jsfiddle.net/SeKT9/
ul
{
margin:0;
padding:0;
list-style:none;
}
body > ul > li
{
float: left;
position: relative;
}
ul > li a
{
display: block;
margin: 5px;
}
ul > li:hover > ul
{
display: block;
}
ul > li > ul
{
position: absolute;
min-width: 100%;
background-color: green;
display: none;
}
ul > li > ul > li
{
display: block;
}

Maybe just adding an inline-block div around each submenu is quicker/easier? I'm always hesitant to add many lines of code for a small simple effect.
<ul>
<li>
<div style='display:inline-block;'>
Item FooBarBaz
<ul class="submenu">
<li><a>sub1</a></li>
<li><a>sub2</a></li>
<li><a>sub3</a></li>
</ul>
</div>
</li>
<li>
<div style='display:inline-block;'>
Item FooBarBazZipBamBop
<ul class="submenu">
<li><a>sub1</a></li>
<li><a>sub2</a></li>
<li><a>sub3</a></li>
</ul>
</div>
</li>
</ul>

Related

How to prevent opening child sub menu in nested menu

I like to prevent opening nested child categories when you open sub categori
for example i have nested tree like this
Test 1
Sub cat 1/1
Sub Sub cat 1/1
Sub cat 1/2
Test 2
Sub cat 2/1
Sub cat 2/2
When i click Test 1 is opening all nested categories how to prevent opening Sub Sub cat 1/1 ?
PS. system wont let me post question because is mostly code , but i dont see reason to spam random text when i can examplain question in 2-3 lines
$('ul li').click(function(e){
e.preventDefault();
$('.menu-wrapper ul', this).toggleClass('menu-close')
$(this).toggleClass('menu-open')
})
ul{
padding: 0px;
list-style: none;
}
ul li{
padding: 5px 0;
}
ul li a{
width: 100%;
display: inline-block;
}
.menu-close{
display: none;
}
/* Main menu */
.menu-main{
width: 250px;
}
.menu-main li{
position: relative;
}
.has-menu:before{
content: '+';
position: absolute;
right: 0;
}
.has-menu.menu-open:before{
content: '-' !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="menu-main">
<li class="has-menu">
Test 1
<div class="menu-wrapper">
<ul class="menu-child menu-close">
<li class="has-menu">
Sub cat 1/1
<div class="menu-wrapper">
<ul class="menu-subchild menu-close">
<li>
Sub Sub cat 1/1
</li>
</ul>
</div>
</li>
</ul>
<ul class="menu-child menu-close">
<li class="">
Sub cat 1/2
</li>
</ul>
</div>
</li>
</ul>
Is this what you want?
$("li").click(function (e) {
e.preventDefault();
e.stopPropagation();
$(this).children("div").children("ul").toggleClass("menu-close");
$(this).toggleClass("menu-open");
});
ul{
padding: 0px;
list-style: none;
}
ul li{
padding: 5px 0;
}
ul li a{
width: 100%;
display: inline-block;
}
.menu-close{
display: none;
/*background-color:red;*/
}
/* Main menu */
.menu-main{
width: 250px;
}
.menu-main li{
position: relative;
}
.has-menu:before{
content: '+';
position: absolute;
right: 0;
}
.has-menu.menu-open:before{
content: '-' !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="menu-main">
<li class="has-menu">
Test 1
<div class="menu-wrapper">
<ul class="menu-child menu-close">
<li class="has-menu">
Sub cat 1/1
<div class="menu-wrapper">
<ul class="menu-subchild menu-close">
<li>
Sub Sub cat 1/1
</li>
</ul>
</div>
</li>
</ul>
<ul class="menu-child menu-close">
<li class="">
Sub cat 1/2
</li>
</ul>
</div>
</li>
</ul>
Use the child-selector:
$('ul li').click(function(e){
e.stopPropagation();
$('> .menu-wrapper > ul', this).toggleClass('menu-close');
$(this).toggleClass('menu-open');
});
(Updated)

toggleClass triggering twice when resizing browser window

I'm currently in the middle of creating a responsive navigation. I've managed to finish and trying to fix an issue. Whenever I tried resizing the browser, it seems the toggleClass seems to be triggering multiple times. If refresh the browser it works OK, but after resizing it seems to trigger a couple of times in one click.
Here is the code that I have been working on.
JSFiddle - https://jsfiddle.net/kvpyzbxr/1/
<header>
<ul class="navigation secondary-navigation">
<li>
Schools
</li>
<li>
Faculty
</li>
<li>
Research
</li>
<li>
Contact Us
</li>
</ul>
<ul class="navigation primary-navigation">
<li>
Programs
<ul>
<li>Degree Programs</li>
<li>Master in Business Administration</li>
<li>Executive Master in Business Administration</li>
<li>Master in Entrepreneurship</li>
<li>Master of Science and Innovation and Business</li>
<li>Master in Development Management</li>
</ul>
</li>
<li>
Admissions
<ul>
<li>How to Apply</li>
<li>Application Form</li>
<li>Scholarship and Financial Aid</li>
</ul>
</li>
<li>
About Us
<ul>
<li>Why AIM</li>
<li>Leadership</li>
<li>Network and Alliances</li>
<li>Our Brand Story</li>
</ul>
</li>
<li>
News
</li>
<li>
Alumni
<ul>
<li>AIM Leader Magazine</li>
<li>My AIM Connect</li>
<li>Triple A Awardees</li>
</ul>
</li>
<li>
Give
<ul>
<li>Make A Gift</li>
</ul>
</li>
</ul>
</header>
<script>
$(document).ready(function() {
function detectMobile() {
if ($(window).width() < 1080) {
$('header').addClass('mobile');
$('.secondary-navigation').insertAfter('.primary-navigation');
}
else {
$('header').removeClass('mobile');
$('.secondary-navigation').insertBefore('.primary-navigation');
}
$('.navigation li').on('click', function() {
console.log('open');
$(this).toggleClass('expand-menu');
})
}
detectMobile();
$(window).resize(function() {
detectMobile();
})
})
</script>
header {
max-width: 1336px;
margin: 0 auto;
}
header .navigation {
padding: 10px 0;
clear: both;
}
header .navigation li {
padding: 10px;
display: inline-block;
position: relative;
font-family: 'Arial', sans-serif;
background-color: #272041;
color: #fff;
float: left;
}
header .navigation li a {
color: #fff;
}
header .navigation li ul {
position: absolute;
top: 0;
left: 0;
padding-top: 30px;
display: none;
}
header .navigation li ul li {
background-color: #231d39;
color: #95939e;
}
header .navigation li:hover ul {
display: block;
}
header.mobile .navigation li {
display: block;
float: none;
}
header.mobile .navigation li ul {
position: static;
display: none;
height: 0;
}
header.mobile .navigation li.expand-menu ul {
height: initial;
display: block;
}
That's because you add the click-event several times. Every time detectMobile() is called you bind a click event to $('.navigation li'). So just move
$('.navigation li').on('click', function() {
console.log('open');
$(this).toggleClass('expand-menu');
})
outside of your detectMobile() function.

How to calculate a speed for slideToggle()

I have a navigation menu with several accordion-style items. Some "parent" links have more children than others. I'd like to vary the speed of the slideToggle() so that the ones with more children take longer to slideDown(). Here is what I tried, but it's jumping around for some reason. There's no easing happening at all, as you can see.
// Get the height of each list and save it in the data-height attribute
$('.main-nav > ul > li > ul').each(function() {
$(this).slideDown(0);
$(this).data('height', $(this).height());
$(this).slideUp(0);
});
$('.main-nav > ul > li').click(function() {
// Multiply the height of the element by the speed desired
$(this).children().slideToggle($(this).data('height') * 1000, 'easeInOutExpo');
});
.main-nav {
position: fixed;
top: 0;
left: 0;
}
.main-nav ul {
list-style-type: none;
padding: 0;
margin: 0;
font-size: 18px;
}
.main-nav ul li {
padding: 22px 0;
cursor: pointer;
}
.main-nav > ul {
padding: 0 22px;
}
.main-nav > ul > li > ul {
display: none;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<nav class="main-nav">
<ul>
<li>Global
<ul>
<li>Typography</li>
<li>Colors</li>
<li>Icons</li>
</ul>
</li>
<li>Elements
<ul>
<li>Text</li>
<li>Buttons</li>
<li>Lists</li>
<li>Tables</li>
<li>Media</li>
</ul>
</li>
<li>Controls
<ul>
<li>dropdown</li>
<li>alerts</li>
<li>badges</li>
<li>modals</li>
</ul>
</li>
<li>Layout
<ul>
<li>dynamic row</li>
<li>flex</li>
</ul>
</li>
<li>Components
<ul>
<li>cards</li>
<li>banners</li>
<li>itemEditor</li>
<li>itemIndex</li>
<li>jQueryUI</li>
<li>login</li>
<li>main</li>
<li>details (and detail views)</li>
<li>drilldown</li>
<li>mega menu</li>
<li>navigation</li>
<li>search</li>
<li>thick items</li>
<li>widgets</li>
</ul>
</li>
</ul>
</nav>
Instead of going based off height, I'd recommend going based off how many li elements are in the ul (you may need to modify the multiplier if 1000 is too slow for you).
// Get the height of each list and save it in the data-height attribute
$('.main-nav > ul > li > ul').each(function() {
$(this).slideUp(0);
});
$('.main-nav > ul > li').click(function() {
// Multiply the height of the element by the speed desired
$(this).children().slideToggle($(this).find("li").length * 1000, 'easeInOutExpo');
});
.main-nav {
position: fixed;
top: 0;
left: 0;
}
.main-nav ul {
list-style-type: none;
padding: 0;
margin: 0;
font-size: 18px;
}
.main-nav ul li {
padding: 22px 0;
cursor: pointer;
}
.main-nav > ul {
padding: 0 22px;
}
.main-nav > ul > li > ul {
display: none;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<nav class="main-nav">
<ul>
<li>Global
<ul>
<li>Typography</li>
<li>Colors</li>
<li>Icons</li>
</ul>
</li>
<li>Elements
<ul>
<li>Text</li>
<li>Buttons</li>
<li>Lists</li>
<li>Tables</li>
<li>Media</li>
</ul>
</li>
<li>Controls
<ul>
<li>dropdown</li>
<li>alerts</li>
<li>badges</li>
<li>modals</li>
</ul>
</li>
<li>Layout
<ul>
<li>dynamic row</li>
<li>flex</li>
</ul>
</li>
<li>Components
<ul>
<li>cards</li>
<li>banners</li>
<li>itemEditor</li>
<li>itemIndex</li>
<li>jQueryUI</li>
<li>login</li>
<li>main</li>
<li>details (and detail views)</li>
<li>drilldown</li>
<li>mega menu</li>
<li>navigation</li>
<li>search</li>
<li>thick items</li>
<li>widgets</li>
</ul>
</li>
</ul>
</nav>
$(this).data('height') appears to be undefined, so it appears that you end up passing NaN as the first arg to the slideToggle() function, I am not sure what the fix is but hopefully that points you in the right direction

How can i make my jQuery code work on all elements

I have this accordion menu but it only works on the first ul. How do I get it to work on all ul like this? If you could explain what I'm doing wrong so I know in the future that would be great.
Also, how do I get it so that the link toggles between two classes, right and down each time it is clicked? It also has the class turq-font on it. I want the right to be removed and replaced with down when the link is clicked and the menu is showing. Heres my code:
$(function() {
$("#show-menu").click(function() {
$("#sub-menu-acc").toggleClass("active-menu", 1000);
});
});
.active-menu {
display: block !important;
}
.admin-area ul li {
margin: 6px 0px;
}
.admin-right-menu ul li a {
color: black;
text-decoration: none;
}
.admin-area ul {
list-style: none;
margin-bottom: 20px;
}
.admin-area ul li ul {
display: none;
padding-left: 20px;
margin-bottom: 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="admin-area">
<div class="admin-right-menu">
<ul>
<li>Stats >
<ul id="sub-menu-acc">
<li>Business Stats
</li>
<li>Affiliate stats
</li>
</ul>
</li>
<li>Reports >
<ul id="sub-menu-acc">
<li>Global
</li>
<li>Sales
</li>
<li>Sales trends
</li>
</ul>
</li>
</ul>
</div></div>
The issue is due to your use of repeated id attributes; they should be unique within a document. Convert your code to use classes instead.
Also note that you should use the this keyword to traverse the DOM to find the ul related to the clicked a element, and the addition of right down in a toggleClass() call. Try this:
$(function() {
$(".show-menu").click(function(e) {
e.preventDefault(); // stop the '#' of the clicked a appearing in the URL
$(this).toggleClass('right down').next(".sub-menu-acc").toggleClass("active-menu");
});
});
.active-menu {
display: block !important;
}
.admin-area ul li {
margin: 6px 0px;
}
.admin-right-menu ul li a {
color: black;
text-decoration: none;
}
.admin-area ul {
list-style: none;
margin-bottom: 20px;
}
.admin-area ul li ul {
display: none;
padding-left: 20px;
margin-bottom: 0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="admin-area">
<ul>
<li>
Stats >
<ul class="sub-menu-acc">
<li>
Business Stats
</li>
<li>
Affiliate stats
</li>
</ul>
</li>
<li>
Reports >
<ul class="sub-menu-acc">
<li>
Global
</li>
<li>
Sales
</li>
<li>
Sales trends
</li>
</ul>
</li>
</ul>
</div>

jQuery using tab to go through dropdown menu links (keyboard accessibility)

I have a css menu I would like to make accessible through keyboard interaction. I want to be able to tab through each link including sub menu links.
If the dropdown focus moves on to the next parent link dropdown then the previous dropdown should hide.
Updated Fiddle
HTML
<ul>
<li class="custom-MainMenu-TopNav-li">
<div>
<span>Parent link 1</span>
<div>
<ul class="custom-MainMenu-SubNav-dropdown">
<li>Sub Link</li>
<li>Sub Link</li>
</ul>
</div>
</div>
</li>
<li class="custom-MainMenu-TopNav-li">
<div>
<span>Parent link 2</span>
<div>
<ul class="custom-MainMenu-SubNav-dropdown">
<li>Sub Link</li>
<li>Sub Link</li>
</ul>
</div>
</div>
</li>
</ul>
JavaScript
accessibleDropdown();
function accessibleDropdown(){
jQuery('.custom-MainMenu-TopNav-li a').each(function(){
jQuery(this).focus(function(){
jQuery(this).addClass('focused');
var menuParent = jQuery(this).parent().next().find('ul');
jQuery(menuParent).css('display','block');
});
jQuery(this).blur(function(){
jQuery(this).removeClass('focused');
});
});
}
I am not sure what is your desired outcome and need for this result, but hopefully this will help you out.
I had to redo your example due to naming convention and approach, but I assume this is what you wanted...
Here's a demo, just in case...
JSFiddle
HTML
<ul class="navbar">
<li class="navbar-item">
Parent Link
<ul class="navbar-sub">
<li class="navbar-sub-item">
One
</li>
<li class="navbar-sub-item">
Two
</li>
<li class="navbar-sub-item">
Three
</li>
</ul>
</li>
<li class="navbar-item">
Parent Link
<ul class="navbar-sub">
<li class="navbar-sub-item">
One
</li>
<li class="navbar-sub-item">
Two
</li>
<li class="navbar-sub-item">
Three
</li>
</ul>
</li>
<li class="navbar-item">
Parent Link
<ul class="navbar-sub">
<li class="navbar-sub-item">
One
</li>
<li class="navbar-sub-item">
Two
</li>
<li class="navbar-sub-item">
Three
</li>
</ul>
</li>
<li class="navbar-item">
Parent Link
<ul class="navbar-sub">
<li class="navbar-sub-item">
One
</li>
<li class="navbar-sub-item">
Two
</li>
<li class="navbar-sub-item">
Three
</li>
</ul>
</li>
</ul>
CSS
body {
margin: 10px;
}
.navbar,
.navbar .navbar-sub {
list-style: none;
margin: 0;
padding: 0;
}
.navbar > .navbar-item {
float: left;
}
.navbar > .navbar-item:last-child {
margin-right: 0;
}
.navbar > .navbar-item.active > .navbar-sub {
display: block;
}
.navbar > .navbar-item a {
text-decoration: none;
}
.navbar > .navbar-item > a {
background-color: #999;
padding: 10px 20px;
color: #696969;
display: block;
}
.navbar > .navbar-item > a:hover,
.navbar > .navbar-item > a:focus,
.navbar > .navbar-item.active > a {
background-color: #ccc;
}
.navbar .navbar-sub {
display: none;
}
.navbar .navbar-sub > .navbar-sub-item > a {
color: #ccc;
display: block;
padding: 5px 10px;
text-align: center;
background-color: #696969;
}
.navbar .navbar-item.active .navbar-sub-item > a:hover,
.navbar .navbar-item.active .navbar-sub-item > a:focus {
background-color: #999;
}
jQuery
$('.navbar').on('mouseenter focusin', '.navbar-item > a', function () {
$(this)
.parent('.navbar-item')
.addClass('active')
.siblings('.navbar-item')
.removeClass('active')
});
here you go, simple jquery :)
// display drop down box when mouse is over
$(".custom-MainMenu-TopNav-li a").mouseover(function(){
$(this).find(".custom-MainMenu-SubNav-dropdown").css("display", "block");
});
// hide drop down box when mouse leaves
$(".custom-MainMenu-TopNav-li a").mouseleave(function(){
$(this).find(".custom-MainMenu-SubNav-dropdown").css("display", "none");
});
This basically displays/hide each the dropdown when the mouse is over/leaves the parent div.
I don't think it would be a good idea to display the dropbown menu on focus, cause i believe you can only focus on certain elements like inputs.
Hope this helps!

Categories