How to calculate a speed for slideToggle() - javascript

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

Related

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.

Custom CSS Navigation dropdown

Currently on my site when I have too many links, the link falls down below the navigation. See my example: https://jsfiddle.net/cn6z13n1/
Is it possible instead to have a More Links list item at the far right which will have a dropdown populated with links?
.toolkit_nav {
background:#dfdfdf;
width:100%;
height:40px;
padding:0;
}
.toolkit_nav ul {
margin:0;
}
.toolkit_nav ul .page_item {
display:inline-block;
line-height:40px;
list-style-type:none;
margin:0px;
padding:0 20px;
}
.toolkit_nav ul .page_item:first-child {
margin-left:0;
padding-left:0;
}
.page_item:hover, .current_page_item {
background:grey;
}
.page_item a {
color:black;
font-size: 0.9em;
font-weight: 400;
text-decoration:none;
}
<nav class="toolkit_nav">
<div class="row">
<div class="medium-12 columns">
<ul>
<li class="page_item page-item-1035 current_page_item">Introduction</li>
<li class="page_item page-item-1039">Digital Landscapes</li>
<li class="page_item page-item-1039">Link 4</li>
<li class="page_item page-item-1039">Link 3</li>
<li class="page_item page-item-1039">Link 2</li>
<li class="page_item page-item-1039">Link 1</li>
<li class="page_item page-item-1039">Link 5</li>
</ul>
</div>
</div>
</nav>
You would need to do this in js i suggest something like this
get the width of the row (max width for nav)
loop through the li elements and sum up there width (+ remember to add the width of a "more" element here
when sum of width > width of nav element hide the elements
add js to your "more" button which shows the hidden elements
Following code is not tested but should give you an idea:
var maxWidth = $('#nav').width();
var moreWidth = $('#more').width(); // li "more" element
var sumWidth = moreWidth;
$('#nav li').each(function() {
sumWidth += $(this).width();
if(sumWidth > maxWidth) {
$(this).addClass('hide'); // add css for hide class
}
});
$('#more').on('click', function() {
$('#nav .hide').fadeIn(100);
// You will need more code here to place it correctly, maybe append the elements in an container
});
Here an example with your fiddle:
https://jsfiddle.net/cn6z13n1/3/
Note: this is just a rough draft, you might to calc paddings etc. to make this work rly good
Edit: updated example with $(window).resize() function
https://jsfiddle.net/cn6z13n1/6/
You'll need to change you HTML slightly but this will work.
.toolkit_nav {
background: #dfdfdf;
width: 100%;
height: 40px;
padding: 0;
}
.toolkit_nav ul {
margin: 0;
}
.toolkit_nav ul .page_item {
display: inline-block;
line-height: 40px;
list-style-type: none;
margin: 0px;
padding: 0 20px;
}
.toolkit_nav ul .page_item:first-child {
margin-left: 0;
padding-left: 0;
}
.page_item:hover,
.current_page_item {
background: grey;
}
.page_item a {
color: black;
font-size: 0.9em;
font-weight: 400;
text-decoration: none;
}
/* NEW STUFF */
.sub-nav,
.sub-nav li {
box-sizing: border-box;
}
.more {
position: relative;
}
.more>ul {
display: none;
position: absolute;
left: 0;
top: 100%;
padding: 0
}
.more:hover>ul {
display: block;
}
.more>ul>li {
display: block;
width: 100%;
clear: both;
text-align: center;
}
.toolkit_nav ul.sub-nav .page_item:first-child {
padding: 0 20px;
}
<nav class="toolkit_nav">
<div class="row">
<div class="medium-12 columns">
<ul>
<li class="page_item page-item-1035 current_page_item">Introduction
</li>
<li class="page_item page-item-1039">Digital Landscapes
</li>
<li class="page_item page-item-1039">Link 4
</li>
<li class="page_item page-item-1039">Link 3
</li>
<li class="page_item page-item-1039">Link 2
</li>
<li class="page_item page-item-1039 more">More...
<ul class="sub-nav">
<li class="page_item page-item-1039">Link 1
</li>
<li class="page_item page-item-1039">Link 5
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>

How to remove jquery cache on hovered item

i'm not sure if i gave the right term. I'm having an issue on how to remove the cache on the hovered item.
Html
<div class="gallery-nav">
<ul>
<li> 01</li>
<li> 02</li>
<li> 03</li>
<li> 04</li>
<li> 05</li>
<li> 06</li>
<li> 07</li>
<li> 08</li>
<li> 09</li>
<li> 10</li>
<li> 11</li>
<li> 12</li>
</ul>
<div class="clear"></div>
</div>
Javascript
function growLine(e) {
e.find('span').stop(false,true).animate({
width: 22
}, 400);
}
function shrinkLine(e) {
e.find('span').stop(false,true).animate({
width: 0
}, 400);
}
$('.gallery-nav li').each(function() {
$(this).append('<span />');
});
$('.gallery-nav li:first-child').addClass('active');
$('.gallery-nav li').hover(function() {
if( !$(this).hasClass('active') ) {
growLine($(this) );
}
}, function() {
if( !$(this).hasClass('active') ) {
shrinkLine( $(this) );
}
});
$('.gallery-nav li a').click( function () {
var allList = $('.gallery-nav li');
var e = $(this);
allList.removeAttr('class');
e.parent().addClass('active');
});
jsFiddle
There is a line below the numbers when hovered.
The problem is the line is not removed once i click on other numbers.
This works but only once.
You are doing animation to set width of span(which is inside li) equal to width=22 and adding classactive to li. You just have to revert this effect.
You need to modify your click event handler so that to set width=0px for span and active class from the li having class="active", see below code
$('.gallery-nav li a').click( function () {
var e = $(this);
$('li.active').find('span').css('width','0px');
$('li.active').removeClass('active');
e.parent().addClass('active');
});
DEMO
try this: Jsfiddle
$('.gallery-nav li:first-child').addClass('active');
$('.gallery-nav li').click( function () {
$(this).addClass('active').siblings().removeClass('active');
});
.gallery-nav ul {
margin: 0;
padding: 0 ;
list-style: none;
}
.gallery-nav li {
float: left;
margin: 0 7px;
}
.gallery-nav li a {
display: block;
position: relative;
text-transform: uppercase;
font: 300 19px 'Lato', sans-serif;
color : #000;
text-decoration: none;
}
.gallery-nav li a:before {
content: '';
position: absolute;
bottom: -2px;
left: 0;
background: red;
height: 1px;
width: 0px;
transition: width .3s ease
}
.gallery-nav li.active > a,
.gallery-nav li:hover > a {
font-weight: 700;
}
.gallery-nav li:hover a:before,
.gallery-nav li.active a:before {
width: 22px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="gallery-nav">
<ul>
<li> 01</li>
<li> 02</li>
<li> 03</li>
<li> 04</li>
<li> 05</li>
<li> 06</li>
<li> 07</li>
<li> 08</li>
<li> 09</li>
<li> 10</li>
<li> 11</li>
<li> 12</li>
</ul>
<div class="clear"></div>
</div>

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

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>

How to reverse the direction of a suckerfish menu (drop-up)

I have a navigation bar at the bottom of my page rather than at the top. I am trying to implement a "drop-up" suckerfish menu. I want to do it with as little javascript as possible. My current code is as follows and works fine as a drop-down menu. Is there a way to reverse it?
Here is my code:
<style type="text/css">
#nav, #nav ul {
padding: 0;
margin: 0;
list-style: none;
}
#nav a {
display: block;
width: 10em;
}
#nav li {
float: left;
width: 10em;
background: #000000;
}
#nav li ul {
position: absolute;
left: -999em;
}
#nav li: hover ul {
left: auto;
}
#nav li:hover ul, #nav li.sfhover ul {
left: auto;
}
</style>
<script type="text/javascript">
sfHover = function() {
var sfEls = document.getElementById("nav").getElementsByTagName("LI");
for (var i=0; i<sfEls.length; i++) {
sfEls[i].onmouseover=function() {
this.className+=" sfhover";
}
sfEls[i].onmouseout=function() {
this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
}
}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);
</script>
<ul id="nav">
<li>Home</li>
<li>About Us
<ul>
<li>Who We Are</li>
<li>What We Believe</li>
<li>Our Pastors</li>
</ul>
</li>
<li>Ministries
<ul>
<li>Victory Kids (VC3)</li>
<li>Victory Youth</li>
<li>Connect Groups</li>
<li>S.O.A.P</li>
<li>Victory Group</li>
</ul>
</li>
<li>Connect
<ul>
<li>Contact Us</li>
<li>Social Networks</li>
<li>Blogs</li>
</ul>
</li>
<li>Media
<ul>
<li>Sermon Podcasts</li>
<li>Videos</li>
<li>Images</li>
</ul>
</li>
<li>Giving
<ul>
<li>Help Support</li>
<li>Ministries We Support</li>
<li>2011 Financial Report</li>
</ul>
</li>
<li>Resources</li>
</ul>
All help is appreciated. Thanks!
<style type="text/css">
#nav, #nav ul {
padding: 0;
margin: 0;
list-style: none;
bottom:0px;
position:absolute;
}
#nav a {
display: block;
width: 10em;
}
#nav li {
float: left;
width: 10em;
background: #000000;
}
#nav li ul {
position: absolute;
left: -999em;
bottom:20px;
}
#nav li: hover ul {
left: auto;
}
#nav li:hover ul, #nav li.sfhover ul {
left: auto;
}
</style>
<script type="text/javascript">
sfHover = function() {
var sfEls = document.getElementById("nav").getElementsByTagName("LI");
for (var i=0; i<sfEls.length; i++) {
sfEls[i].onmouseover=function() {
this.className+=" sfhover";
}
sfEls[i].onmouseout=function() {
this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
}
}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);
</script>
<ul id="nav">
<li>Home</li>
<li>About Us
<ul>
<li>Who We Are</li>
<li>What We Believe</li>
<li>Our Pastors</li>
</ul>
</li>
<li>Ministries
<ul>
<li>Victory Kids (VC3)</li>
<li>Victory Youth</li>
<li>Connect Groups</li>
<li>S.O.A.P</li>
<li>Victory Group</li>
</ul>
</li>
<li>Connect
<ul>
<li>Contact Us</li>
<li>Social Networks</li>
<li>Blogs</li>
</ul>
</li>
<li>Media
<ul>
<li>Sermon Podcasts</li>
<li>Videos</li>
<li>Images</li>
</ul>
</li>
<li>Giving
<ul>
<li>Help Support</li>
<li>Ministries We Support</li>
<li>2011 Financial Report</li>
</ul>
</li>
<li>Resources</li>
</ul>
Try this without additional JS

Categories