collapsing an accordion "li" when class is removed - javascript

I've been trying to sort through this for a while now, can't seem to get it working 100%. The current code reveals the .content area without any collapse. It also shifts the page down to center on the content (which in this case goes under the fold if you don't).
$(document).ready(function() {
$('.vl-option .vl-toggle-link').click(function(e) {
e.preventDefault();
$(this).closest('li').find('.content').not(':animated').slideToggle();
$(this).closest('li').toggleClass('active');
});
$('.vl-option').bind('click',function(){
var self = this;
setTimeout(function() {
theOffset = $(self).offset();
$('body,html').animate({ scrollTop: theOffset.top - 20 });
}, 310);
});
});
I've attempted a few approaches to no avail. I'll list a few below (concept was to find any siblings and collapse):
$(this).siblings('li').find('.content').slideToggle();
which actually breaks the original functionality. Then I went with the following (to try an make anything without class="active" collapse):
if ( $(this).siblings('li').not('.active').find('.content').slideToggle(); ) //also tried .hide()
which doesn't seem to have any affect on anything.
The HTML is simple
<ul>
<li class="lv-option active"><!-- when toggled, "active" class is applied... -->
Yada
<div class="content"></div>
</li>
<li class="lv-option"><!-- ...when untoggled, "active" class removed -->
Yada yada
<div class="content"></div>
</li>
</ul>
.active is only applied for stylistic reasons. it has no effect on the functionality of anything. Just needed to be able to target the :before / :after when something was selected.
I just can't seem to wrap my head around jquery... argh.

Only one content at a time should be opened in an accordion right, so applying .slideToggle() to all <li> contents will break this rule. I think it's okay with your markup since you only have two <li>, so they just slides alternately. But if you have more I think the active <li> only should be .slideToggle() other should be .slideUp() only.
You can just chain them all:
$('.vl-toggle-link').click(function(e) {
e.preventDefault();
//find the sibling content apply slideToggle, then move to their parent and add active
$(this).siblings('.content').slideToggle().parent('li').addClass('active')
// go through all the siblings of their parent and remove active, then slideUp their child content
.siblings('li').removeClass('active').children('.content').slideUp();
});
I also find your .animate({scrollTop}) not working properly because it's obtaining the old offset().top value. I think you should get the offset().top after the slideToggle() has finished. See this jsfiddle.
Or you can also calculate scrollTop: jsfiddle.

Related

gsap menu to scroll to elements only moves down as expected, not up

I've got a page using gsap to animate scrolling.
To navigate, the menu is setup with a simple "scroll to ID" approach which is intercepted by gsap to do the scrolling.
This works as expected to scroll down the page, but not up the page. However if you've scrolled down the page, selecting the element above from the menu doesn't scroll up. But selecting an element 2 up from the current one, then scrolls up 1 element.
An example of the code;
<nav>
<ul>
<li>Section 1</li>
<li>Section 2</li>
</ul>
</nav>
<section id="section1" class="panel">
</section>
gsap.utils.toArray("nav a").forEach(function(a) {
a.addEventListener("click", function(e) {
e.preventDefault();
gsap.to(window, {duration: 1, scrollTo: e.target.getAttribute("href")});
});
});
A demo is here on codepen
This is a logical issue. Once you've scrolled past a section, the element has been moved down by 100vh. So when you navigate to its y offset, it's 100vh below where it was at the start.
There are different ways to fix it. The easiest may be to keep an array of values of the original offset and scrollTo those values instead. Demo.
Sides notes:
You can just use the selector strings as your targets in GSAP when you don't need it scoped further.
You should use a double colon (::) for pseudo-elements in CSS.
If you're going to use ES6 features like const some places, you might as well use them throughout your code.
You're more likely to get help even quicker in the GreenSock forums.

Drop down div doesn't stay visible

I am trying to make a drop down list and I have made it somewhat work. When I put the mouse over the area, a div in the shape of the drop down becomes visible. Then when you put your mouse over anything in the div, it disappears. That is obviously not meant to happen. Here is my code. Any solution is greatly appreciated.
HTML:
<li><a onMouseOver="showServersDropDown()" onClick="showServersDropDown()" class="three-d">
Servers
<span aria-hidden="true" class="three-d-box">
<span class="front">Servers</span>
<span class="back">Servers</span>
</span>
</a></li>
<div onMouseOut="hideServersDropDown()" id="serversDropDown">
<p>Live Map</p>
</div> <!--This ends the Server List Drop Down Div-->
JS:
function showServersDropDown() {
document.getElementById("serversDropDown").style.display="block";
}
function hideServersDropDown() {
document.getElementById("serversDropDown").style.display="none";
}
I wasn't able to reproduce this exact issue, but it sounds like the problem is caused by hovering over the child elements of the div firing the onmouseout event of the parent div. I found this answer that should help you with that: prevent onmouseout when hovering child element of the parent absolute div.
I also noticed that you are changing display to none. Once the display is set to none, the div won't render at all on the browser, which will prevent mouse events from firing on it, so hovering in that area will not cause it to reappear. I found another answer here about hovering over a hidden element to reveal it: Hover over a hidden element to show it.
Also, it seems like you are missing an onmouseover event to reveal the drop down list when you hover over it, although I may be mistaken in what you are trying to accomplish.
So in all, with two modifications to your Javascript and a small modification to your HTML, I think you can achieve your intended result with this:
<div onmouseout="hideServersDropDown(event)" onmouseover="showServersDropDown(event)" id="serversDropDown">
<p>Live Map</p>
</div> <!--This ends the Server List Drop Down Div-->
function showServersDropDown(event) {
document.getElementById("serversDropDown").style.opacity="1";
}
function hideServersDropDown(event) {
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this) {
return;
}
document.getElementById("serversDropDown").style.opacity="0";
}
I only put the event blocking code in hideServersDropDown since you would want the onmouseover event to fire and show whether you are hovering over a parent or a child in the div. I hope this helps!
It's usually because the mouse is leaving the original div, the key is to make the submenu a child of the main div:
<ul class="menu">
<li>
<a>nav title</a>
<ul>
<li><a>sub link</a></li>
</ul>
</li>
</ul>
Then in pure css you can handle this:
.menu ul { display: none }
.menu li:hover ul { display: block }

Trouble creating an expanding list with jQuery

I need to make a list for my client's podcast archive page that opens up to reveal links to the different podcasts for the month they click on. I pretty much want exactly something like BlogSpot has for their default blog archive widget on the right side of the page here: http://kimrome.blogspot.com/
I was able to make something like that here: http://thehummingbirdplace.com/test2.html but I'm not sure how to make the arrows that show if a list has been expanded or not. So it needs to change direction when it's clicked and return to the previous direction when it's clicked again to close that section.
My version also has the child elements showing when I open the page, and I don't want them to expand until their parent is clicked on.
I've look online to see if there is jQuery already created to do this, or how I might be able to make it, but since I'm not sure what this whole thing is properly titled, I get mixed results. Any help would be appreciated!!
Try jQuery-UI accordion
$(...).accordion();
, or this: http://jsfiddle.net/5SKLV/1/
$(...).myAccordion();
Just write CSS at your taste.
If you would like to do this yourself (it's fun to write things yourself):
I've added an ID of #tree to the root <ul>, and wrapped the text of the level 1 <li>s in <span>:
<ul id="tree">
<li>
<span>parent1</span>
<ul>
<li>child11</li>
<li>child12</li>
</ul>
</li>
<li>
<span>parent2</span>
<ul>
<li>child21</li>
<li>child22</li>
</ul>
</li>
</ul>
To apply arrows that point left and right to the parent elements, create two CSS classes with backgrounds, for example (you'll need to find the background images elsewhere or make your own):
.opened > span {
background: url('arrow_pointing_down.png') left top;
color: #0a0; /* just to make it easy to know which class it has */
}
.closed > span {
background: url('arrow_pointing_right.png') right top;
color: #00a; /* just to make it easy to know which class it has */
}
To hide all the child elements when the page loads...
$('#tree > li').addClass('closed');
// hide the level 2 ul's
$('#tree > li ul').hide();
Then in your click handler:
$("#tree > li > span").click(function (event) {
event.preventDefault();
// swap the opened and closed classes
$(this).parent().toggleClass('opened closed');
// toggle the level 2 ul instead of li
$(this).parent().find("ul").toggle();
});
Working demo here: http://jsfiddle.net/cTLGN/
ADDITIONAL:
This demo code doesn't make use of caching references to jQuery objects to make it easier to read. In reality instead of doing:
$(this).parent().toggleClass('opened closed');
$(this).parent().find("ul").toggle();
... one should do:
var parent = $(this).parent(); // search the DOM once for this' parent, and remember it
parent.toggleClass('opened closed');
parent.find("ul").toggle();
.. because every time you use jQuery's $() constructor it needs to search thru the entire DOM, which can be quite expensive if you do it repeatedly.

Get height of DIV and its content with JQuery

I have a div which contains several elements:
<div class="menuItem designMenu" id="dMenu">
<ul class="menuList menu">
<li class="menuHeader">Design</li>
<li class="projectHeader">Mother</li>
<li class="projectBody">Some text here</li>
<li class="more">More</li>
</ul>
</div>
I need to get the height of the dMenu items that I can animate it upwards, including all the content inside. My Javascript currently:
var designHeight = $("#dMenu").height();
Returns nothing.
I've tried offsetHeight, scrollHeight, and everything else Google turns up. I'm running the jQuery at the end of the body, inside a document ready function.
The reason to get the height to animate, instead of doing it manually, is that a: I'd like to learn how and b: I don't yet know how many items will be in the div.
Are the lis within the ul floated? If so, and the parent does not have overflow: hidden the parent collapses and therefor has no height. Try (if possible) adding overflow: hidden to the parent element, or hard code a height in your CSS.
I'm using scrollHeight to get at what I want. I also, embarrassingly, noticed as I was fiddling with things I was loading my JS before loading JQuery. So ashamed I didn't spot that.

jquery dropdown div not quite working

i'm trying to make a div drop down when someone hovers over a link. Inside the div is a login form. The following code works only in that if i hover over the link the div does appear. However when i move the mouse from the link down over the div, the div immediately retracts. Please see:
jQuery(document).ready(function() {
jQuery('.slidedown').hide();
jQuery('a.top-link-cart').hover( function(){ // enter animation
jQuery('.slidedown').stop(true,true).animate({
height: ['toggle', 'swing'],
}, 600, function() { /* animation done */ });
}, function(){ // leave animation
jQuery('.slidedown').mouseout( function() {
setTimeout( function(){
jQuery('.slidedown').stop(true,true).animate( {
height: '0px'}, 600, function(){});}, 200 ); // setTimeout ends here
}); // mouseout ends here
});
});
All i'm trying to achieve is have the div a) stay open if the user mouses from the link to the div b)close if the user moves mouse away from link but not into div and c) close if user moves mouse out of div. I thought the .mouseout function would keep the div open so that i can at least move my mouse over it but it isn't working. Any ideas? I'd be very grateful this has been a headache to me for a week now. Thanks.
You should not use .hover but .mouseover() instead for your first method.
You could wrap your link and the div that does the animation in another div and then apply the hover to the parent div instead of the link. This way you will still validate. For example:
<div class="whatever">
<a class="top-link-cart">Show login form</a>
<div class="slidedown">form html goes here</div>
</div>
and the javascript would be:
jQuery(document).ready(function(){
jQuery('.slidedown').hide();
jQuery('.whatever').hover(function(){//to show
jQuery('.slidedown').show('effect', duration in millisecs);
}, function(){//to hide
jQuery('.slidedown').hide('effect', duration in millisecs);
});
});
this uses the jQueryUI for the animation effects, but you could use the .slideDown() and .slideUp() methods as well if all you need is the div to slide up or down
You need to nest your div.slidedown inside the a.top-link-cart:
<a class="top-link-cart">Show login form
<div class="slidedown">
The login form HTML
</div>
</a>
Ignoring standards (block elements like <div> tags shouldn't really be nested inside inline elements like <a> tags), this will work because when the div.slidedown expands, so does the parent <a>.
That way, the mouseout event won't be triggered until the user's mouse leaves the <a>.

Categories