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>
Related
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.
I have a nav bar in the form of a ul with several li elements and with the last li (named 'more') having its own sub-menu in the form of another ul. I was trying (and was successful) in making it so that the sub-menu's original state is visibility:visible; and then when the user clicks on li name 'more' it would toggle between visibility: visible; and visibility: hidden;
I used JavaScript and a counter with an if statement. The reason why I used the counter was because when I tried:
if(document.querySelector('#subMenu').style.visibility == "hidden")...;
But it wouldn't toggle.
My questions are:
Would this method of creating the toggle function be deemed acceptable in a professional front end developer workplace?
Is there a better way to toggle between visible and hidden on clicking an element using JavaScript ONLY (trying to get better at JavaScript)?
The code is as follows(I have only included the relevant code):
HTML
<ol id = "leftNav" class = "bothNavs">
<li class = "navs" id = "more">More<div class = "arrow"></div>
<ul class = "subMenu">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
</li>
</ol>
CSS
.subMenu {
width: 160%;
padding: 5px 0px;
padding-left: 7px;
margin-left: 6px;
position: absolute;
top: 100%;
left:0px;
visibility: hidden;
box-shadow: 0px 2px 3px rgba(0,0,0,0.2);
background: #2D2D2D;
list-style: outside none none;
z-index: 1001;
}
JavaScript
var more = document.querySelector('#more');
var subMenu = document.querySelector('.subMenu');
var counter =0;
more.addEventListener("click", toggle);
function toggle () {
if(counter === 0){
subMenu.style.visibility = "visible";
counter += 1;
} else {
subMenu.style.visibility = "hidden";
counter -= 1;
}
};
Thank you in advance for your answers.
I feel a better way of toggling visibility (or any style) is to toggle a class.
Consider something like this in your CSS:
.subMenu.is-visible {
visibility: visible;
}
Then your function just needs to look like this:
var menu = document.querySelector('.subMenu');
document.getElementById('more').addEventListener('click', function () {
menu.classList.toggle('is-visible');
}, false);
For a toggle you should use a boolean value rather than int:
var more = document.querySelector('#more');
var subMenu = document.querySelector('.subMenu');
var clicked = false;
more.addEventListener("click", function(evt) {
// This is called a ternary operator, it's basically
// a really simple if/else statement
subMenu.style.visibility = (clicked) ? "visible " : "hidden";
// This will set clicked to the opposite (not) value of what
// it currently is. Being that we're using a boolean
// it will toggle true/false
clicked = !clicked;
});
You should also read (and vote for) this - What is the difference between visibility:hidden and display:none?
You should not embed a <ul> inside a <li> element. There's all sorts of reasons why, but mostly it messes with the hierarchy and makes the HTML less meaningful.
Also the last time I had to tackle this problem it ended up looking something like this:
var subMenu = document.querySelector('.subMenu');
if (clicked) {
subMenu.style.visibility = "visible";
} else {
subMenu.style.visibility = "hidden";
}
Consider making it id instead of class incase you have other submenus it interferes with.
If you don't really need a counter, use a boolean:
var more = document.querySelector('#more');
var subMenu = document.querySelector('.subMenu');
more.addEventListener("click", toggle);
function toggle () {
subMenu.style.visibility = subMenu.style.visibility != "hidden" ? "hidden" : "visible";
};
.subMenu {
width: 60%;
background: #2D2D2D;
list-style: outside none none;
color: white
}
<ol id = "leftNav" class = "bothNavs">
<li class = "navs" id = "more">More<div class = "arrow"></div>
<ul class = "subMenu" style="visibility: hidden">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
</li>
</ol>
Ideally, you'd modify this code to toggle a class instead of an inline CSS style, as James suggested in his answer.
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.
I'm trying to make a stupid horizontal nav bar with a drop-down on some of the items. The way I decided to do it is just by putting the drop-down in a div tag. This is easily changeable, i just don't like to go heavy on the html side.
Basically I just want my drop down to work when you hover over the parent element. Additional css is going to be used to make it pretty and positioned better.
Here's my js:
var dropdown = $('.dropdown');
var parent = dropdown.parent();
$(parent).hover(
function () {
dropdown.css('display', 'block');
}
);
Here's my css:
div.nav {
text-align: center;
}
div.nav > ul > li {
margin-top: 15px;
text-align: center;
font-size: 1.25em;
}
div.nav > ul > li {
display: inline-block;
list-style-type: none;
}
div.nav a {
padding: 1em;
}
div.dropdown {
display: none;
background-color: black;
position: absolute;
}
Here's my html:
<div class="nav">
<ul>
<li>Home</li>
<li>
Sample Game
<div class="dropdown">
About it
<br>
Game
</div>
</li>
<li>TP Solutions</li>
<li>Projects</li>
<li>Contact Me</li>
</ul>
<div class="clear"></div>
You should not be using "parent" as a variable name, as it's a reserved word.
$(document).ready(function() {
var $dropdown = $('.dropdown'),
$parent = $dropdown.parent();
$parent.on("mouseover",
function () {
$dropdown.css('display', 'block');
}
);
$parent.on("mouseout",
function () {
$dropdown.css('display', 'none');
}
);
});
According to the oreder this has to be done:
add a jQuery plugin first
Then add your script
so the order will be like this:
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js'>
</script>
<script>
$(function(){
var dropdown = $('.dropdown');
var parent = dropdown.parent();
$(parent).hover(function () {
dropdown.css('display', 'block');
});
});
</script>
Please try the below code.
$(".nav").on("mouseenter","li",function(){
$(this).find(".dropdown").show();
});
$(".nav").on("mouseleave","li",function(){
$(this).find(".dropdown").hide();
});
In your code " dropdown.parent(); " -> this will refer all the parents which have child dropdown and will show the menu. we need to refer current hover parent. Please check the working example in below link.
http://jsfiddle.net/renjith/wX48f/
There are so many good solutions to use jQuery and CSS to show a drop down menus. So you don't need to reinvent the wheel. Here are some examples that you might be able to find one to fit your need.
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.