I have a UL-LI e.g.
<ul>
<li id="1">item-1</li>
<li id="2">item-2</li>
<li id="3">item-3</li>
<li id="4">item-4</li>
</ul>
I would like to move one of the items to another position in the list. e.g. item-2 to AFTER item-4.
Normally I can do this by deleting the item and then appending it after another.
But I would like to do this to happen visually with animation. As in, item-2 descends to after item-4.
How can I achieve this?
IDs should not start with numbers...
$('#two').slideUp(500, function () {
$('#four').after(this);
$(this).slideDown(500);
});
Here is a demo: http://jsfiddle.net/jasper/8JFBA/
Or if you always want to add the element to the end:
$('#two').slideUp(500, function () {
$('ul').append(this);
$(this).slideDown(500);
});
Here is a demo: http://jsfiddle.net/jasper/8JFBA/1/
Update
Ok, so if you want the element to slide to it's new location here ya go:
//absolutely position the element and give it a top property so it doesn't go to the top of the container
$('#two').css({ position : 'absolute', top : $('#two').position().top });
//now get the offset to the bottom of the list by getting the top offset and height for the last list-item
var lastOffset = ($(this).children().last().position().top + $(this).children().last().height());
//now animate the element to the new position
$('#two').animate({ top : lastOffset }, 1000, function () {
//when the animation is done, re-add the element to the new position in the list and reset it's position and top values
$(this).appendTo('ul').css({ position : 'relative', top : 0 });
});
And a demo: http://jsfiddle.net/jasper/8JFBA/3/
Update
You can animate not only the element being moved to the end of the list but you can animate the rest of the list items as they move up:
var $LIs = $('ul').children(),
liHeight = 20;
$LIs.on('click', function () {
var index = ($(this).index()),
$LIsAfter = $LIs.filter(':gt(' + index + ')');
console.log(index);
$(this).css({ position : 'absolute', top : $(this).position().top });
$.each($LIsAfter, function (i) {
$(this).css({ position : 'absolute', top : ((i + index + 1) * liHeight) });
});
$(this).stop(true, true).animate({ top : (($LIs.length - 1) * liHeight)}, 1000, function () {
$(this).appendTo('ul').css({ position : 'relative', top : 0 });
});
$.each($LIsAfter, function (i) {
$(this).stop(true, true).animate({ top : ((index + i) * liHeight) }, 1000, function () {
$(this).css({ position : 'relative', top : 0 });
});
});
});
Here is a demo: http://jsfiddle.net/jasper/8JFBA/8/
This isn't quite complete, there is still a bug or two, but it should help get anyone started on the idea.
I tried to implement a smoother transition when you descend and below is my version..
You need to try out the demo to understand how it works.. Select value from the drop down and hit Descend to see the animation.
DEMO
Edit: Updated top position of $from before addClass('active') to start from the exact position and not top: 0px. Thanks to Jasper for finding this issue.
var $from = $('#from');
var $to = $('#to');
$('button').click (function () {
var from = $from.val();
var to = $to.val();
var $li = $('ul li');
var $fromEl = $('#' + from);
var $toEl = $('#' + to);
//only descending
if (from == to || $li.index($fromEl) > $li.index($toEl)) return;
var destX = $toEl.position().top;
$toEl.after('<li id="tmpLi2"></li>');
$('#tmpLi2').animate({height: $fromEl.outerHeight()}, 1000);
//add a blank li for smooth animation
$fromEl
.after('<li id="tmpLi1"> </li>')
.css ('top', $fromEl.position().top)
.addClass ('active' )
.animate({
top: (destX)
},
1000,
function() {
$toEl.after(this);
$('#tmpLi2').remove();
$(this).removeClass('active');
});
$('#tmpLi1').slideUp(function() { $(this).remove()});
});
Related
I'm trying to expand on previous js code you all helped me out with in the past. The code creates a slideshow of cars and their pricing on a webpage, pulling the data from a mysql db. I had it working where it would redirect to a new page after a minute of time, but now I want the redirect to happen when the total number of plays equals the number of slides * 2. I am not experienced with js and any help would be great...
Here is the code that creates slideshow... (jscript.js)
$(document).ready(function(){
var currentPosition = 0;
var slideWidth = 1280;
var slides = $('.slide');
var numberOfSlides = slides.length;
// Remove scrollbar in JS
$('#slidesContainer').css('overflow', 'hidden');
// Wrap all .slides with #slideInner div
slides
.wrapAll('<div id="slideInner"></div>')
// Float left to display horizontally, readjust .slides width
.css({
'float' : 'left',
'width' : slideWidth
});
// Set #slideInner width equal to total width of all slides
$('#slideInner').css('width', slideWidth * numberOfSlides);
// Insert controls in the DOM
$('#slideshow')
.prepend('<span class="control" id="leftControl"></span>')
.append('<span class="control" id="rightControl"></span>');
// Hide left arrow control on first load
manageControls(currentPosition);
// Create event listeners for .controls clicks
$('.control')
.bind('click', function(){
// Determine new position
currentPosition = ($(this).attr('id')=='rightControl') ? currentPosition+1 : currentPosition-1;
// Hide / show controls
manageControls(currentPosition);
// Move slideInner using margin-left
$('#slideInner').animate({
'marginLeft' : slideWidth*(-currentPosition)
}, function() {
// if last slide then move the pointer to 1st slide
if(currentPosition == numberOfSlides-1) {
currentPosition = -1;
}
});
});
window.setInterval(function() {
$('#rightControl.control').click();
}, 3000);
// manageControls: Hides and Shows controls depending on currentPosition
function manageControls(position){
// Hide left arrow if position is first slide
if(position==0){ $('#leftControl').hide() } else{ $('#leftControl').show() }
// Hide right arrow if position is last slide
if(position==numberOfSlides-1){ $('#rightControl').hide() } else{$('#rightControl').show() }
}
});
Im thinking the code to redirect would be something like... (nextpage.js)
$(document).ready(function(){
var currentPosition = 0;
var slides = $('.slide');
var numberOfSlides = slides.length;
var clicks = $('.control');
var slidesPlayed = clicks.length;
if(numberOfSlides == (slidesPlayed * 2)){
window.location.href = "https://www.cars.com";
}
});
Any help would be greatly appreciated.
Thank you!
Freshly tested:
$(document).ready(function(){
var slidesPlayed = 0;
var whatever = 5; //that would be the total slides * 2 if I understand what you want, but let's say it's 5 for this example
$('#thetrigger').on("click", function() {
slidesPlayed++;
console.log(slidesPlayed);
if(slidesPlayed >= whatever){
window.location.href = "https://www.cars.com";
}
});
});
If you click #trigger whatever times, you get redirected.
I have a simple menu that scrolls to a section when a menu item is clicked.
For example:
<li>Contact</li>
Will scroll to:
<section id="contact"></section>
However I've also got a menu that always sticks to the top, and the height of that menu is not calculated with the scroll script. So the menu always hovers over the top part of an element, making part of it unreadable.
How can I change it so that it scrolls not directly to that element, but let's say 80px above it? And preferable only for one element, since I've only got this issue for one section on my page.
I am using this scroll to element script:
/* Scroll to Main Menu
================================================== */
$('#navigation a[href*=#]').click( function(event) {
var $this = $(this);
var offset = -80;
if($this.parent().is(':nth-child(2)')) {
offset = 2; // for second child dont do offset
};
$.scrollTo( $this.attr('href') , 650, { easing: 'swing' , offset: offset , 'axis':'y' } );
event.preventDefault();
});
/* Scroll to Element on Page
================================================== */
$('a#to-blog').click( function(event) {
event.preventDefault();
$.scrollTo( $('#blog') , 1250, { offset: 1 , 'axis':'y' } );
});
$('.hero-btn').click( function(event) {
var $this = $(this);
var offset = -80;
if($this.attr('href') == '#about-us' || $('.nomenu').is(':visible')) {
offset = 0; // for first section dont do offset
};
$.scrollTo( $this.attr('href') , 650, { easing: 'swing' , offset: offset , 'axis':'y' } );
event.preventDefault();
});
/* Add active class for each nav depending on scroll
================================================== */
$('section').each(function() {
$(this).waypoint( function( direction ) {
if( direction === 'down' ) {
var containerID = $(this).attr('id');
/* update navigation */
$('#navigation a').removeClass('active');
$('#navigation a[href*=#'+containerID+']').addClass('active');
}
} , { offset: '80px' } );
$(this).waypoint( function( direction ) {
if( direction === 'up' ) {
var containerID = $(this).attr('id');
/* update navigation */
$('#navigation a').removeClass('active');
$('#navigation a[href*=#'+containerID+']').addClass('active');
}
} , { offset: function() { return -$(this).height() - 80; } });
});
I believe you need to change the offset in the various places to the value you want:
var offset = -80;
A lesser number will be further "up" the page, a higher number will be further "down" the page.
This code makes the 'li' move like I want to but when I get to the last 'li' and click next it keeps moving and the 'li' moves out of the users view. I want to know how to loop it so the first 'li' shows up again.
<script type="text/javascript">
$(document).ready(function() {
$('#right').click(function() {
$('.carousel-inner li').animate({
right: '+=292px'
}, 500);
});
$('#left').click(function() {
$('.carousel-inner li').animate({
right: '-=292px'
}, 500);
});
});
</script>
Here is a Fiddle to see an example
This should solve your problem:
$(document).ready(function () {
var inner = $('.carousel-inner'),
slides = inner.find('li'),
width = slides.eq(0).outerWidth(true),
max = (width * slides.length) - width;
$('#right, #left').on('click', function () {
var currRight = parseInt(inner.css('right'), 10), move;
if (this.id === 'right') {
move = currRight === max ? 0 : currRight+width;
} else {
move = currRight === 0 ? max : currRight-width;
}
inner.animate({ right: move }, 500);
});
});
The top four lines cache elements and set up a few basic variables such as the max right value that can used and the width of the slides.
I've then combined the click events to avoid repetition. The first line of the click event defines currRight as $('.carousel-inner')'s current CSS right value as well as move which is used later on.
if (this.id === 'right'){ /* #right */ }else{ /* #left */ } checks whether the id of the clicked element is right or left. The code inside the if statement just checks to see whether the slider is at zero (the beginning) or max (the end) and then sets the move variable to the correct value.
Here it is working: http://jsfiddle.net/mFxcq/10/
Update: To make the slides move on a timer add this after the click event is attached:
function setTimer(){
clearTimeout(window.slideTimer);
window.slideTimer = setTimeout(function(){ $('#right').trigger('click'); }, 5000);
};
setTimer();
Then add setTimer(); right after inner.animate({ right: move }, 500); and everything will work as expected.
Here it is working: http://jsfiddle.net/mFxcq/14/
Add a totalItems variable which will represent the total number of items in the carousel, and a currentItem variable which will represent the number of the current item being displayed (i.e. a number from 1 to totalItems). Then, simply check if it's the last item, and if it is, move the position back to the first one. Check out the revised fiddle, where it works in both directions. Here's example JavaScript, with everything written out for clarity.
var totalItems = $('.carousel-inner li').length;
var currentItem = 1;
$('#right').click(function () {
if (currentItem === totalItems) {
// We've reached the end -- go to the beginning again
$('.carousel-inner li').animate({
right: '-=' + 292 * (totalItems-1) + 'px'
}, 500);
currentItem = 1;
} else {
$('.carousel-inner li').animate({
right: '+=292px'
}, 500);
currentItem += 1;
}
});
$('#left').click(function () {
if (currentItem === 1) {
// We're at the beginning -- loop back to the end
$('.carousel-inner li').animate({
right: '+=' + 292 * (totalItems-1) + 'px'
}, 500);
currentItem = totalItems;
} else {
$('.carousel-inner li').animate({
right: '-=292px'
}, 500);
currentItem -= 1;
}
});
Take a look at this fiddle for a working approach. For the right click.
Basically, each time you click "Right", you'll test to compare the distance traveled by the items and compare that to the maximum distance allowed based on the total number of items.
var slideWidth = 292;
$('#right').click(function() {
if(parseInt($('.carousel-inner li').css('right')) == slideWidth*($('.carousel-inner li').length-1)) {
$('.carousel-inner li').animate({
right: '0px'
, 500);
}
else {
$('.carousel-inner li').animate({
right: '+=' + slideWidth + 'px'
}, 500);
}
});
I'm animating a div to left by 0px by clicking on the div colored in red. Below the div , classes are added to li's as the div moves along, but the classes gets added to only certain li's and not all.
Is there any other logic to fix this ?
Fiddle - http://jsfiddle.net/AsfFQ/16/
Below is the image of the issue
Try this jsFiddle example.
var pos;
var timer, selectLi = (function() {
var $block = $('.block'),
$container = $('.container'),
$lis = $('.container ul li'),
liWidth = $lis.width(),
$selectedLi;
return function() {
pos = $block.offset().left - $container.offset().left;
liNum = Math.round(pos / liWidth);
// $selectedLi && $selectedLi.removeClass('selected');
$selectedLi = $($lis.get(liNum));
$('li.eligible').each(function() {
if ($block.offset().left-3 <= $(this).offset().left) $(this).addClass('selected');
});
};
})();
$('.block').click(function() {
timer = setInterval(selectLi, 30);
$(this).animate({
left: 0
}, function() {
clearInterval(timer);
});
});
$('li').each(function() {
$(this).addClass('eligible');
if ($(this).offset().left > $('.block').offset().left) $(this).removeClass('eligible');
});
This sets the eligible list items and then as the bar moves, compares their position to tjat of the bar and if they're in range, they get the class added.
Your little animation needs only a little code.
See jsfiddle example
var $block = $('.block'),
start = $block.offset().left;
$block.one('click').animate({left: 0})
.$('li').filter(function(){return $(this).offset().left<=start})
.repeat(30).filter(function(){return $(this).offset().left>=$block.offset().left})
.addClass('selected').unrepeat();
I'm using this plugin jquery-timing.
This also works when animating 100px on each click, see another fiddle:
var $block = $('.block');
$block.on('click').animate({left: '-=100px'})
.$('li').filter(function(){return $(this).offset().left<=$block.offset().left})
.repeat(30).filter(function(){return $(this).offset().left>=$block.offset().left})
.addClass('selected').unrepeat();
Have fun!
example page
I have a floating menu that i've built to the left side (green),
and i've made it start moving after 200 pixels. and now i need to to stop
and not go over the footer (blue) area.
any ideas how to make my JS better?
this thing is, I cannot check this on the scroll event, because of the animation
going on after i scroll, so it needs to be done someway else.
so how to make the animation stop at the end just before the footer?
I've resolved the issue perfectly (hope so)
with the help of you guys, and released
a jQuery plugin for floating sticky boxes:
http://plugins.jquery.com/project/stickyfloat
$.fn.menuFloater = function(options) {
var opts = $.extend({ startFrom: 0, offsetY: 0, attach: '', duration: 50 }, options);
// opts.offsetY
var $obj = this;
$obj.css({ position: 'absolute' /*, opacity: opts.opacity */ });
/* get the bottom position of the parent element */
var parentBottomPoint = $obj.parent().offset().top + $obj.parent().height() ;
var topMax = $obj.parent().height() - $obj.innerHeight() + parseInt($obj.parent().css('padding-top')); //get the maximum scrollTop value
if ( topMax < 0 ) {
topMax = 0;
}
console.log(topMax);
$(window).scroll(function () {
$obj.stop(); // stop all calculations on scroll event
// console.log($(document).scrollTop() + " : " + $obj.offset().top);
/* get to bottom position of the floating element */
var isAnimated = true;
var objTop= $obj.offset().top;
var objBottomPoint = objTop + $obj.outerHeight();
if ( ( $(document).scrollTop() > opts.startFrom || (objTop - $(document).scrollTop()) > opts.startFrom ) && ( $obj.outerHeight() < $(window).height() ) ){
var adjust;
( $(document).scrollTop() < opts.startFrom ) ? adjust = opts.offsetY : adjust = -opts.startFrom + opts.offsetY;
// and changed here to take acount the maximum scroll top value
var newpos = ($(document).scrollTop() + adjust );
if ( newpos > topMax ) {
newpos = topMax;
}
$obj.animate({ top: newpos }, opts.duration, function(){ isAnimated = false } );
}
else {
$obj.stop();
}
});
};
In the $(window).scroll function have you checked whether the bottom position of the floating div is less than or equal to the top position of the footer element.