Variable menu length - javascript

Brand new to jquery. I'm trying to make a drop-down animated menu that opens up just enough to show submenus while dropping the menu items as well. For example:
Item1
Item2
Item3
And when you mouse over Item2 you get
Item1
Item2
Subitem1
Subitem2
Item3
I know my code needs a lot more work (I'm playing/experimenting now) and my problem is when trying to use a variable the menu doesn't open up at all so the problem is somewhere with the height: variable line.
$(document).ready(function(){
//When mouse rolls over
$("li").mouseover(function(){
$(this).stop().animate({height: $('ul > li').length * 20})
});
//When mouse is removed
$("li").mouseout(function(){
$(this).stop().animate({height:'18px'})
});
});
I'd actually prefer to make separate variable lines such as
$(document).ready(function(){
var numitems = $('ul > li').length;
var totalheight = numitems * 20;
//When mouse rolls over
$("li").mouseover(function(){
$(this).stop().animate({height: totalheight})
});
//When mouse is removed
$("li").mouseout(function(){
$(this).stop().animate({height:'18px'})
});
});

try this:
$(document).ready(function(){
var numitems = $('ul > li').length;
var totalheight = (numitems * 20)+'px';
//When mouse rolls over
$("li").mouseover(function(){
$(this).stop().animate({height: totalheight},500); //don't forget to give the animation some duration
});
//When mouse is removed
$("li").mouseout(function(){
$(this).stop().animate({height:'18px'},500);
});
});

Forget about trying to store the height of the elements and multiplying up. You will run into serious problems if you do this in the future.
You can always get the height of an element with jQuery's outerHeight() property. But if you want this to happen on the mouse enter/out, you would be tidier with the .hover() function of jQuery and do the following :
var speed = 500;
$("li.top").hover(function () {
var ul = $(this).find("ul");
$(ul).stop(); // stop any current animation
// tidy up attributes that have been added
$(ul).removeClass("flattened");
$(ul).css("height", ""); // very necessary;
$(ul).css("display", ""); // very necessary;
$(ul).removeClass("show");
var _height = $(ul).outerHeight();
$(ul).addClass("flattened");
$(ul).addClass("show");
$(ul).show();
$(ul).animate({ height: _height }, speed , function () {
$(ul).removeClass("flattened");
});
}, function () {
var ul = $(this).find("ul");
$(ul).stop();
$(ul).animate({ height: "0px" }, speed , function () {
$(ul).hide();
$(ul).css("height", ""); // very necessary;
$(ul).css("display", ""); // very necessary;
$(ul).removeClass("show");
});
});
I used the following CSS to make things work in my test file:
ul li a { float: left; width: 100%; }
ul li > ul { display: none;float:left; }
ul li { float: left; width: 100%; border: solid 1px black; min-height: 0; }
.flattened { height: 0px; overflow:hidden; }
And the following HTML:
<nav>
<ul>
<li class="top">Item 1</li>
<li class="top">Item 2
<ul>
<li>SubItem 1</li>
<li>SubItem 2</li>
</ul>
</li>
<li class="top">Item 3</li>
</ul>
</nav>
Hope this works for you.

Related

Animate append to list

How could I animate the moving from one list to another, ideally using Jquery animate? Given the following code I know I should be able to use animate to move the listitem to the position it would take up in the receiving list... but how do I get the coordinates, and translate them into css values?
$('#list1').on('click', 'li', function() {
// $(this).animate( ??? )
$(this).appendTo($('#list2'));
})
$('#list2').on('click', 'li', function() {
// $(this).animate( ??? )
$(this).appendTo($('#list1'));
})
ul {
background-color: grey;
color: black;
padding: 10px;
margin: 50px;
width: 20%;
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="list1">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
<ul id="list2">
<li>four</li>
<li>five</li>
<li>six</li>
</ul>
jsfiddle
It's Saturday evening (local-time) and I want to help someone, I took your question seriously and provided an example along with explanations at each step below.
The most important thing in this example is to use an absolute positioned list-element (in this example #animatedList) so that this animated list-element does not interact with other elements. Furthermore getting the correct coordinates you have to take margins/paddings of the original elements into consideration as well.
You have to click on the Button "Run code snippet" to execute this example. Hope this helps you:
$('#list1').on('click', 'li', function() {
var startElement = $(this);
var endElement = $('#list2');
startAnimation(startElement, endElement);
})
$('#list2').on('click', 'li', function() {
var startElement = $(this);
var endElement = $('#list1');
startAnimation(startElement, endElement);
})
function startAnimation(startElement, endElement){
var clickedListElement = startElement;
// copy content of original element into animated list-element
var contentCopy = clickedListElement.text();
var startCoords = getCoordsOfElement(clickedListElement);
// if there are no li-elements anymore take the position of the whole list instead otherwise take last list-element
var endCoords;
if(endElement.find('li:last').length)endCoords = getCoordsOfElement(endElement.find('li:last'));
else endCoords = getCoordsOfElement(endElement);
// hide original element because the animated list-element will be shown
clickedListElement.css('visibility','hidden');
showAnimation(contentCopy,startCoords,endCoords,function(){
// the last step: copy the original element to it's destination and show it again because the animation is over
clickedListElement.appendTo(endElement).css('visibility','visible');
});
}
function getCoordsOfElement(element){
var endCoords = element.offset();
// because the margin and paddings of these lists distorts the calculated position you have to take them into consideration
// and therefore we subtract them for top and left
var marginOffsetTop = 50;
endCoords.top -= marginOffsetTop;
var marginOffsetLeft = 50;
endCoords.left -= marginOffsetLeft;
// the same happens with padding
var paddingOffsetTop = 10;
endCoords.top -= paddingOffsetTop;
var paddingOffsetLeft = 10;
endCoords.left -= paddingOffsetLeft;
return endCoords;
}
function showAnimation(contentCopy,startCoord,endCoord,callback){
var list = $('#animatedList');
// copy text of originel element into animated-list-element
list.find('li').text(contentCopy);
// set animated-list/element at the same position as the original clicked element
list.css('top',startCoord.top+'px').css('left',startCoord.left+'px').show();
list.animate({
top: endCoord.top+'px',
left: endCoord.left+'px'
}, 1000, function(){
// animation is finished here
list.hide();
callback();
});
}
ul {
background-color: grey;
color: black;
padding: 10px;
margin: 50px;
width: 20%;
display: inline-block;
}
#animatedList{
display:none;
position:absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="list1">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
<ul id="list2">
<li>four</li>
<li>five</li>
<li>six</li>
</ul>
<ul id="animatedList">
<li></li>
</ul>

How do I animate moving a list item to the top?

I have a list and would like to move the item to the top when it's clicked on. Simultaneously, all other items should move down to make space.
That's what I have so far:
<ul id="list">
<li id="one">item-1</li>
<li id="two">item-2</li>
<li id="three">item-3</li>
<li id="four">item-4</li>
</ul>
//
$('li').on('click', function () {
$(this).css({ position : 'absolute', top : $(this).position().top });
var height = $(this).parent().children().first().height();
var list = $(this).parent();
$(this).animate({top: '0px'}, { duration: 500, queue: false });
list.children().each(function( index, element) {
var $liElement = $(element);
if($liElement != $(this))
{
$liElement.animate({top: height + 'px'}, { duration: 500, queue: false });
}
});
});
Here is the link: http://jsfiddle.net/5qgnjvdp/
I see the item moving on top but all others don't move at all. What is wrong here?
Should I use prepend() to insert the list item on top when the animation is finished?
While not really an "animation", I was working on something some time ago along similar lines. What I ended up using was a slideUp, then a slideDown to give a sense of animation without actual animation connected to the move.
$('li').click(function() {
$(this).slideToggle(500, function() {
$(this).prependTo('#list').slideToggle(500); });
});
li { list-style: none; background: #ddd;padding: 5px 10px; border: 1px solid #aaa; margin: 10px; text-transform: uppercase; font-weight: bold;color:#fff; }
#list { margin:20px; padding:0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<ul id="list" data-role="listview">
<li id="one">item-1</li>
<li id="two">item-2</li>
<li id="three">item-3</li>
<li id="four">item-4</li>
</ul>
$('li').click(function() {
$(this).slideToggle(500, function() {
$(this).prependTo('#list').slideToggle(500); });
});
fiddle update
Again, I realize this isn't really an animation beyond the slide toggles, but it conveys a sense of interactivity similar to an animation.
There are a couple of problems with your script.
When comparing $liElement != $(this) you are in the scope of the each function where this refers to the current list child, which is the same as element. Store the clicked li in a variable outside of the looping function and compare with that. Also no need to compare the $(...) objects. element != clickedLi would suffice.
When you animate the other li elements they don't have position: absolute (or relative) so you won't see their movement.
If you assign the other li elements an absolute position you move all of them to the same position, the second row in the list. You could use position relative to move them down. You should just make sure to exclude the elements that come after the original position of the clicked li.
After the animation is complete you should insert the list item as the first child of the list and then remove the absolute/relative positioning, including the top style to make sure it works a second time (and to ensure that the visible representation is in line with the elements' structure.

Change Active Menu Item on Page Scroll?

I want to activate the menu item when I get to it's corresponding section. I got inspired by this previous SO question: Change Active Menu Item on Page Scroll? .
but the difference is that in my menu I have a little image over each menu item, that shows only if I hover the menu item, and hides when don't.
HTML
<nav>
<ul id="pics">
<li id="text-what"><img src="images/what.png" id="pic-what" class="vishid"><p>item1</p></li>
<li id="text-training"><img src="images/training.png" id="pic-training" class="vishid"><p>item2</p></li>
<li id="text-testi"><img src="images/trait.png" id="pic-testi" class="vishid"><p>item3</p></li>
<li id="text-contact"><img src="images/contact.gif" id="pic-contact" class="vishid"><p>item4</p></li>
</ul>
</nav>
CSS
.vishid{
visibility: hidden;
}
.visvis{
visibility:visible;
}
JAVASCRIPT (to show and hide images when hovering items)
$(document).ready(function(){
$("#text-what").hover(function(){
$("#pic-what").addClass('visvis');
},function(){
$("#pic-what").removeClass('visvis');
});
$("#text-training").hover(function(){
$("#pic-training").addClass('visvis');
},function(){
$("#pic-training").removeClass('visvis');
});
$("#text-testi").hover(function(){
$("#pic-testi").addClass('visvis');
},function(){
$("#pic-testi").removeClass('visvis');
});
$("#text-contact").hover(function(){
$("#pic-contact").addClass('visvis');
},function(){
$("#pic-contact").removeClass('visvis');
});
});
I want to show the image when I am at it's corresponding section. How can I do that with javascript?
There is a lot going on here. Your HTML should technically be corrected. href's should not encapsulte LI's. Instead your href should be set to block - width and height 100% - within the LI. Let's also move the class of .vishid to the parent LI. That way if you want it to effect anything else - besides just the images - in the future, it would be easy to add. So that would look like:
<nav>
<ul id="pics">
<li id="text-what" class="vishid"><img src="images/what.png" id="pic-what"><p>item1</p></li>
<li id="text-training" class="vishid"><img src="images/training.png" id="pic-training"><p>item2</p></li>
<li id="text-testi" class="vishid"><img src="images/trait.png" id="pic-testi"><p>item3</p></li>
<li id="text-contact" class="vishid"><img src="images/contact.gif" id="pic-contact"><p>item4</p></li><
</ul>
</nav>
Then you need to adjust your CSS to correct for the "non-block" level href.
#pics li a {
display: block;
width: 100%;
height: 100%;
}
.vishid img {
visibility: hidden;
}
.visvis img {
visibility: visible;
}
Finally, I am going to assume that you are using "articles" in your HTML for the sections. Doesn't have to be, but that is what my example will assume.
var clickScroll = false,
triggerHighlight = 80; // distance from the top to trigger action
$(window).scroll(function () {
var y = $(this).scrollTop(),
yCatch = y + triggerHighlight;
// Let's wrap in a variable check. Set this to tru is clicking on navigation
// false if simply scrolling
if (!clickScroll) {
$('article').each(function (i) {
var whichArticle = $(this).attr('id');
if ($(this).position().top < yCatch) {
var currentArticle = "#" + whichArticle;
adjustSubNav(currentArticle);
}
});
}
});
function adjustSubNav(l) {
$('#pics a').each(function (i) {
if ($(this).attr('href') == l) { // Add active class to the corresponding menu item
$(this).parent('li').removeClass('vishid').addClass('visvis');
} else {
$(this).parent('li').removeClass('visvis').addClass('vishid');
}
});
}

jQuery fadeIn elements one at a time with onClick

I'm trying to create a really basic slideshow where the user can click a back or next arrow to see the previous or next element. I have found various questions similar to this but they are automated transitions; I'm looking for the element to switch one at a time onclick. If next is clicked it should go forward, if back is clicked, it should go back. If someone clicks back from the first time, it should show the last; if they click Next when they're on the last one, it will go back to the first.
The code I have currently cycles through multiple elements. I'm new at this so I'm having trouble figuring out how to get it to go only one at a time. This has to work with jQuery 1.3.2, as the site I uses has that library loaded and we can't update it.
It doesn't have to be LI, could be div too if that's better for some reason. Any suggestions on how to achieve are appreciated.
here is a link to my fiddle, but basic HTML would be:
<div id="arrow-next"></div>
<div id="arrow-back"></div>
<ul class="services">
<li style="display: block;">slide 1</li>
<li>slide 2</li>
<li>slide 3</li>
<li>slide 4</li>
<li>slide 5</li>
<li>slide 6</li>
</ul>
css:
ul.services li{
height: 500px;
width: 980px;
border: 1px solid #ccc;
text-align: center;
list-style-type: none;
position: absolute;
top: 0;
left: 0;
display: none;
background-color: #fff;
}
ul.services {
display: inline-block;
position: relative;
}
jquery:
$(document).ready(function() {
$("#arrow-next").click(function() {
$('ul.services li').fadeOut(function(){
$(this).next().fadeIn('slow');
});
});
$("#arrow-back").click(function() {
$('ul.services li').fadeOut(function(){
$(this).prev().fadeIn('slow');
});
});
});
Thanks in advance for any help you can offer. I found this post and tried to update like so, but it does not change what happens with the code I already had.
$(document).ready(function() {
$("#arrow-next").click(function() {
$('ul.services li').fadeOut(function(){
$(this).next().fadeIn('slow', function(){
$(this).prev().fadeOut('slow');
});
});
});
$("#arrow-back").click(function() {
$('ul.services li').fadeOut(function(){
$(this).prev().fadeIn('slow');
});
});
});
something like this?
$(document).ready(function () {
$("#arrow-next").click(function () {
$('ul.services li:visible').fadeOut(function () { //select only the visible element
var $next = $(this).next(); //get the next element
var $target = $next.length ? $next : $('ul.services li:first'); //if there is no next then switch the pointer to point to the first one.
$target.stop(true,true).fadeIn('slow'); //now fadeIn the target element.
});
});
$("#arrow-back").click(function () {
$('ul.services li:visible').fadeOut(function () {
var $prev = $(this).prev(); //get the prev element
var $target = $prev.length ? $prev : $('ul.services li:last');//if there is no next then switch the pointer to point to the last one.
$target.stop(true,true).fadeIn('slow');//now fadeIn the target element.
});
});
});
Fiddle
The problem is that this part:
$('ul.services li')
matches all the <li> elements so they all fade out and then the next element (for each one) fades in.
If you do this:
$("#arrow-next").click(function() {
$('ul.services li:visible').fadeOut(function(){
$(this).next().fadeIn('slow');
});
});
$("#arrow-back").click(function() {
$('ul.services li:visible').fadeOut(function(){
$(this).prev().fadeIn('slow');
});
});
you will be able to move up and down but "up" on the last one won't fadein the first one. (The key difference is the :visible added to the selector.)
Finally, add one more bit to make it roll around at the end by checking whether one faded in by doing the normal stuff and then, if not, fade the first (or last) one in.
$("#arrow-next").click(function () {
$('ul.services li:visible').fadeOut(function () {
$(this).next().fadeIn('slow');
if ($('ul.services li:visible').length == 0) {
$('ul.services li:first').fadeIn('slow');
}
});
});
$("#arrow-back").click(function () {
$('ul.services li:visible').fadeOut(function () {
$(this).prev().fadeIn('slow');
if ($('ul.services li:visible').length == 0) {
$('ul.services li:last').fadeIn('slow');
}
});
});
FIDDLE
Here how I would do it:
HTML:
<div><span class="leftArrow">< </span><span class="rightArrow"> ></span></div>
<div class="slideContainer">
<div class="slide1 slide">slide 1</div>
<div class="slide2 slide">slide 2</div>
<div class="slide3 slide">slide 3</div>
<div class="slide4 slide">slide 4</div>
</div>
JavaScript:
$('.slide').hide();
$('.slide1').show().addClass('active');
var eq=0;
$('.leftArrow').on('click',function(){
$('.active').hide("slide", { direction: "right" }, 500);
if(eq==0){
$('.slide').removeClass('active');
eq=3;
$('.slide:eq('+eq+')').addClass('active');
}
else{
eq=eq-1;
$('.slide').removeClass('active');
$('.slide:eq('+eq+')').addClass('active');
}
$('.active').show('slide',{direction:"left"},500);
});
$('.rightArrow').on('click',function(){
$('.active').hide("slide", { direction: "left" }, 500);
if(eq==3){
$('.slide').removeClass('active');
eq=0;
$('.slide:eq('+eq+')').addClass('active');
}
else{
eq=eq+1;
$('.slide').removeClass('active');
$('.slide:eq('+eq+')').addClass('active');
}
$('.active').show('slide',{direction:"right"},500);
});
You can see the working example here: http://jsfiddle.net/PHrct/

Jquery UL Scroll

I'm trying to use Jquery to have scroll on a UL list, with two span to move up and down.
it works for one li child, but how for an ul filled dynamically ?
thank you, i'm totally lost
$('span.scrollDown').click(function () {
$('.liste-grostitres li:first-child').css('margin-top', 0 - $('.liste-grostitres li').outerHeight());
$('.liste-grostitres').css('overflow', 'hidden');
});
$('span.scrollUp').click(function () {
$('.liste-grostitres li:first-child').css('margin-top', 0);
$('.liste-grostitres').css('overflow', 'visible');
});
<div id="grostitres">
<div class="gInner">
<span class="scrollUp"></span>
<span class="scrollDown"></span>
<div class="scrollable" id="divlist" runat="server">
<ul>
<li></li>
<li></li>
...
</ul>
</div>
</div>
heres a fiddle with slidetoggle:
http://jsfiddle.net/RMQLM/
also the working code example:
HTML:
<div id="up">up</div>
<div id="list">
<ul>
<li>foo1</li>
<li>bar1</li>
<li>foo2</li>
<li>bar2</li>
<li>foo3</li>
<li>bar3</li>
<li>foo4</li>
<li>bar4</li>
<li>foo5</li>
</ul>
</div>
<div id="down">down</div>
CSS:
div#list {
height: 93px;
overflow: hidden;
border: 1px solid red;
}
jQuery:
var listcount = $('li').size();
var cli = 1;
$('#down').click(function() {
if (cli < listcount) {
$('li:nth-child(' + cli + ')').slideToggle();
cli++;
}
});
$('#up').click(function() {
if (cli > 1) {
cli--;
$('li:nth-child(' + cli + ')').slideToggle();
}
});
Set your UL to be position: relative; and have top: 0;.
Add a function to handle the animation:
var scroll_ul = function(offset) {
// Target the UL to scroll
var to_scroll = $('#divlist').find('ul');
// Store the distance to scroll (assumes LIs are all equal height)
var scroll_distance = $('#divlist').find('li').outerHeight(true);
// Animate
to_scroll.stop().animate({ top: '-=' + (offset * scroll_distance) });
};
Then change your click handlers to be something like this:
$('span.scrollDown').click(function() {
scroll_ul(1);
});
$('span.scrollUp').click(function() {
scroll_ul(-1);
});
You may experience strange scroll distances if you hammer the scrollDown/scrollUp buttons. That's when you should look into jQuery's .one() function.
I think it would be more efficient to animate the whole UL instead of individual LIs. You already wrap the UL in a DIV, so why not animate the UL relative to the wrapper? That would work the same way as animating a single LI inside UL, so you don't need to reinvent the wheel.

Categories

Resources