Stop .animate() on last image - javascript

I'm trying to make a gallery of images to scroll using up/down buttons. So far I go the animation but now I need to make it stop on the first and last image.
This is what I got so far, jsfiddle.net/sJDMq/47 (don't mind the buttons, I still need to work on them but they are there, top and bottom red boxes)
Thanks!
$(document).ready(function() {
$(".down_button").click(function () {
$(".scroll-products").animate({marginTop: '-=700px'}, 300);
});
$(".up_button").click(function () {
$(".scroll-products").animate({ marginTop: '+=700px' }, 300);
});
});

I wouldn't have done it this way but just going with what you started with I would just let this JS do the trick:
$(document).ready(function(){
var curIndex = 0;
var height = 700;
var maxHeight = 0;
var allImages = document.getElementsByClassName("sidebar_images");
for(var i=0; i<allImages.length; i++) {
maxHeight += allImages[i].offsetHeight;
}
var maxIndex = Math.ceil(maxHeight/height);
$(".down_button").click(function () {
if(curIndex < maxIndex) {
$(".scroll-products").animate({marginTop: '-='+height+'px'}, 300);
curIndex++;
}
});
$(".up_button").click(function () {
if(curIndex > 0){
$(".scroll-products").animate({ marginTop: '+='+height+'px' }, 300);
curIndex--;
}
});
});
I just updated your fiddle here.

Related

js animate function scrolls to bottom of div instead of top

js windows animate scrolltop to div class is scrolling right to to the bottom of the div
js
$(document).ready(function () {
$('html, body').animate({
scrollTop: $('.tariff').offset().top
}, 'slow');
});
it is scrolling right to the bottom of div class 'tariff'. I want it to scroll and stop at the top of the div.
Did a lot of googling. Don't know where I am going wrong.
EDIT
I think it may have something to do with my external js.js file. Something here is probably effecting the animate scrolling height. Because if I omit this file,
it scrolls properly to the top of the div.
But I don't know what is interfering here.
js.js
$('document').ready(function(){
$('#smshare').click(function() {
$('#smp').slideToggle('fast');
$('#smp').load('social-media-share.php');
});
$('#offer,#orderlink').click(function()
{
$('#order').fadeIn('fast');
$('html, body').animate({
scrollTop: $("#order").offset().top
}, 2000);
$('.scrolltop').fadeIn('fast');
$('.aboutus,.tariff,.services,.contactus,.logged_order,.pers_dets,.clothes_submitted,.locations_list').fadeOut('fast');
});
$('.scrolltop').click(function(){
$('#order').fadeOut('fast');
$('html, body').animate({
scrollTop: $(".header").offset().top
}, 2000);
$('.scrolltop').fadeOut('fast');
});
$('#svl').click(function()
{
$('html, body').animate({
scrollTop: $(".service_locations").offset().top
}, 2000);
});
$('.more1').click(function() {
$('.more_dry_cleaning').slideToggle('fast');
$('.more1').fadeOut('fast');
});
$('.less1').click(function() {
$('.more_dry_cleaning').fadeOut('fast');
$('.more1').fadeIn('fast');
});
var valid=0;
$('form[name=orderform]').submit(function(e) {
// alert('Got you!');
$('.clothesqty').each(function(){
// alert('Got you!');
if($(this).val() != 0) {valid+=1;}
});
if(valid!=0){
// e.preventDefault();
// alert(valid + " inputs have been filled");
return true;
}
else {
e.preventDefault();
//alert('Houston we have contact!');
alert("error: you must fill in at least one field");
return false;
}
});
var oblen=Object.keys(item).length;
var zero=0;
var cloth = "";
var i;
var it=1;
var totitems=0;
var tot=0;
var totcost=0;
var itemid="";
var itemclass="";
var totval=0;
for (var key in item) {
if (!item.hasOwnProperty(key)) {
//The current property is not a direct property of p
continue;
}
var k=key;
var v=item[key];
// alert(v);
cloth = k.replace(/ |-|&|\//g, '').toLowerCase();
itemid="#" + cloth;
itemclass="#" + cloth + "cost";
// alert(v+"/"+itemid);
$(itemid).keyup((function (id, cls,itm,val) {
return function () {
var x = $(id).val();
if(x!=0){
itemtot=x * val;
// alert(itm+"->"+itemtot + "=" + x + "x" + val);
$(cls).css("background-color", "#446666").css("padding","3px").css("color","white").css("width","60px").html("Rs." + itemtot);
}else{
$(cls).css("background-color", "#446666").css("padding","3px").css("color","white").css("width","60px").html("Rs." + zero);
}
}
} (itemid, itemclass,cloth,v)));
}
$("input").each(function() {
$(this).keyup(function(){
// calculateCost();
calculateSum();
});
});
});
//Totals
function calculateSum() {
var sum = 0;
var totcost=0;
//iterate through each textboxes and add the values
$("input").each(function() {
//add only if the value is number
if(!isNaN(this.value) && this.value.length!=0 && this.value.length<=3) {
sum += eval(parseInt(this.value));
}
});
$("div.ittot").each(function() {
var tots=0;
var str=$(this).text();
var thenum = str.replace( /^\D+/g, '');
var tots = parseInt(thenum,10);
//add only if the value is number
//if(!isNaN(tots) && tots.length!=0) {
totcost += tots;
//}
});
// alert(totcost);
$("#totalcost").css("background-color", "green").html("<div style='float:left;margin-left:5px'><span style='font-weight:700'>Total Cost : </span>Rs."+ totcost + "</div><div style='float:right;margin-right:5px'><span style='font-weight:700'>Total Items : </span>" + sum + "</div><div style='clear:both'></div>");
}
//slider2
var myPicIndex = 0;
picCarousel();
function picCarousel() {
var i;
var x = getElementsByClassName("myPicSlides");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
myPicIndex++;
if (myPicIndex > x.length) {myPicIndex = 1}
x[myPicIndex-1].style.display = "block";
setTimeout(picCarousel, 3000); // Change image every 2 seconds
}
function getElementsByClassName(className) {
var found = [];
var elements = document.getElementsByTagName("*");
for (var i = 0; i < elements.length; i++) {
var names = elements[i].className.split(' ');
for (var j = 0; j < names.length; j++) {
if (names[j] == className) found.push(elements[i]);
}
}
return found;
}
//slider1
var myIndex = 0;
carousel();
function carousel() {
var i;
var x = getElementsByClassName("mySlides");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
myIndex++;
if (myIndex > x.length) {myIndex = 1}
x[myIndex-1].style.display = "block";
setTimeout(carousel, 3000); // Change image every 2 seconds
}
function getElementsByClassName(className) {
var found = [];
var elements = document.getElementsByTagName("*");
for (var i = 0; i < elements.length; i++) {
var names = elements[i].className.split(' ');
for (var j = 0; j < names.length; j++) {
if (names[j] == className) found.push(elements[i]);
}
}
return found;
}
EDIT 2
I have further narrowed down the problem to the slider2 JS functions which seem to be a problem. Because if I remove the slider html which i have put in a slider2.php file and am including into the tariff.php file, then the scroll behaves properly.
slider2.php
<div style="width:100%;margin:0 auto">
<img class="myPicSlides" src="images/header/clothes-drying-on-line-iv.jpg" alt="Laundry Clothes Drying">
<img class="myPicSlides" src="images/header/new/shirts-on-line-iv.jpg" alt="Laundry Foam">
<img class="myPicSlides" src="images/header/iron-and-clothes-iv.jpg" alt="Iron Clothes">
<img class="myPicSlides" src="images/header/woollens-iv.jpg" alt="Woollens Laundry">
<img class="myPicSlides" src="images/header/shirts-on-hanger-iv.jpg" alt="Laundry Shirts mylaundrywala">
</div>
EDIT 3
I believe the animate function is taking into consideration the heights of the images in the slider and adding them up ! How to resolve that ?
If you want to reach to the top of window, please use this:
$("html, body").animate({
scrollTop: 0
}, 500);
$('.tariff').offset().top gives position distance between top of body and top of div with class "tariff".
As a result, it covers that much distance when scrolled.
Please try like this.
Use this code.
$(document).ready(function () {
$('html, body').animate({
scrollTop: $('.tariff').offset().top + $('.tariff').height()
}, 'slow');
});
Check the fiddle its is working for me there. Let me know if this helps.
$("#scroll").click(function(e) {
$('html, body').animate({
scrollTop: $('.tariff').offset().top
}, 'slow');
});
Fiddle
Since I realized that it was the heights of the images in the slider which was being laid out one below the other by html rather than fading in and out one by one when javascript is called (which has a fractional delay), the animate function registers the page height including all the heights of the images ! And scrolls keeping those heights in consideration. That is why it was scrolling right down to the bottom. Instead of top of the div.
Though I was not able to find a js solution to the real problem, I managed to find a work around.
Finally, what I did was to delay the slider images loading by using 'load' function. And placed one static image there till slider loaded and then faded the static image out after slider images loaded.
During this period, the animate function picks up only the height of that one static image which is actually the height that div should have after all images have loaded.
Now the animate scroll function is scrolling to the top of the relevant div.
Thanks all for your efforts to help
Vanilla way
var el = document.getElementById('myelement');
el.scrollTop = el.scrollHeight;

jQuery animation for each element

I do not have much experience in animation on Jquery. I want to make a simple animation that will highlight my text line by line with the possibility of stopping. I know how to do something like this for one line but I have no idea how to deal with loop.
here is my code:
var lines = $('#page')[0].getClientRects();
for (var i=0, max = lines.length; i < max; i++)
{
$('#under_liner')
.queue(function() {
$(this).css('top', lines[i].bottom).dequeue();
})
.animate({
width: lines[i].right - lines[i].left
}, 1000 )
.queue(function() {
$(this).css('width', 0).dequeue();
});
}
and jsfiddle http://jsfiddle.net/mz03kfua/2
I don't know if this is exactly what you are looking for, but here's how I'd do it.
Make a function that does the underlining
Make a recursive call on animation callback
Create a global variable to keep count of the current underlined line
Add a boolean that stops the function when false
var lines = $('#page')[0].getClientRects();
var play = true;
var index = 0;
underlineLine();
$('button').click(function(){
play = !play
if(play){
underlineLine()
$(this).html("STOP")
}else{
$(this).html("CONTINUE")
}
})
function underlineLine(){
if(index >= lines.length) return
if(play){
$('#under_liner').css('top', lines[index].bottom).dequeue();
$('#under_liner').css('width','0px');
$('#under_liner').animate({
width: lines[index].right - lines[index].left
}, 1000, function(){
underlineLine(index++)
})
$('#under_liner').css('width', 0).dequeue();
}
}
HERE IS A FIDDLE WITH THE CODE.
Hope it helps.
http://jsfiddle.net/mz03kfua/4/
var lines = $('#page')[0].getClientRects();
var current = 0;
var element;
function animateLine() {
if(typeof lines[current] !== "object") {
return;
}
var line = lines[current];
element = jQuery("<div />", {"class": "under_liner"}).prependTo("#page");
element.css({top: line.bottom}).animate({width: line.width}, 1000, function() {
current++;
animateLine();
});
}
function stopLine(e) {
e.preventDefault();
element.stop(true);
}
jQuery(".stop").click(stopLine);
animateLine();

Shift in course each of div

I'm trying to animate the margin-left of each div one after another:
var array = ['#lazer', '#ergonomic', '#myagkaya-chast', '#krepkost'];
var i = 0;
while (i < array.length) {
array[i].animate({
marginLeft: '40px'
}, 1000, function () {
i++;
});
}
FIDDLE
You need to remove the i++ from the animate callback since it is asynchronous, also rather then extend the animate effect use jQuery.delay() to offset the start time.
var array = ['#lazer', '#ergonomic', '#myagkaya-chast', '#krepkost'];
var i = 0;
var delay = 100;
while (i < array.length) {
$(array[i]).delay(delay * i).animate({
marginLeft: '40px'
}, "linear");
i++;
}
Demo
You forgot your jQuery object wrapper $(array[i]). Also, if you're not going to use a for loop like shown, put your i variable outside the animate() function.
var array = ['#lazer', '#ergonomic', '#myagkaya-chast', '#krepkost'];
for (var i = 0; i < array.length; i++) {
$(array[i]).animate({
marginLeft: '40px'
}, 1000);
}
JSFiddle

$.each animation running separately

I'm trying to run each animation function one after the other instead of all at once.
This is what I've got so far:
$(document).ready(function(){
var bars = $('.bar');
bars.each(function(){
var widthpercent = $(this).attr("data-percent");
$(this).fadeIn();
$(this).animate({width:widthpercent},500);
});
});
I've tried using .delay() and setTimeout() in various combinations to no avail.
Could anyone point me in the right direction? Thank you!
It sounds to me like you're looking for animate's complete function. You can write a recursive function to keep calling the function in the complete function until all the items have been animated. To simplify: every time one element is animated, a callback is fired that animates the next element. That is the purpose of the complete parameter, so I'm certain that is what you're looking for.
Here's an example you can adapt to your specific needs.
Live demo here (click).
var $divs = $('div');
function animate(element) {
$(element).animate({height: '30px'}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
}
var current = 0;
animate($divs[current]);
Further, this same logic can be applied to your fadeIn. Just wrap fadeIn's callback around that logic, like this:
Live demo here (click).
var $divs = $('div');
function animate(element) {
$(element).fadeIn(function() { //now the animation is a callback to the fadeIn
$(element).animate({height: '70px'}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
});
}
var current = 0;
animate($divs[current]);
And here's your code: live demo here (click).
$(document).ready(function(){
var $divs = $('.bar');
function animate(element) {
$(element).fadeIn(function() { //you could unwrap this depending on what you're looking for
var widthpercent = $(element).attr("data-percent");
$(element).animate({
width:widthpercent,
duration: '500ms'
}, {
complete: function() {
if (current < $divs.length-1) {
++current;
animate($divs[current]);
}
}
});
}); //end fadeIn callback
}
var current = 0;
animate($divs[current]);
});
Try this:
var animate = function (el) {
return function () {
var widthpercent = el.data('percent');
el.fadeIn();
el.animate({
width: widthpercent
}, 500);
}
}
var bars = $('.bar');
bars.each(function (index) {
var $this = $(this);
setTimeout(animate($this), index * 500);
});
Fiddle
$(document).ready(function(){
var bars = $('.bar');
bars.each(function(i){
var widthpercent = $(this).attr("data-percent");
$(this).delay(i*800).animate({width:widthpercent,opacity:1,},500);
});
});
This will animate after delaying 800 * i milliseconds.
See this JSFiddle example.

javascript 'over-clicking' bug

I have a bug in Javascript where I am animating the margin left property of a parent container to show its child divs in a sort of next/previous fashion. Problem is if clicking 'next' at a high frequency the if statement seems to be ignored (i.e. only works if click, wait for animation, then click again) :
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
//roll margin back to 0
}
An example can be seen on jsFiddle - http://jsfiddle.net/ZQg5V/
Any help would be appreciated.
Try the below code which will basically check if the container is being animated just return from the function.
Working demo
$next.click(function (e) {
e.preventDefault();
if($contain.is(":animated")){
return;
}
var marLeft = $contain.css('margin-left'),
$this = $(this);
if (marLeft === (-combinedWidth + (regWidth) + "px")) {
$contain.animate({
marginLeft: 0
}, function () {
$back.fadeOut('fast');
});
} else {
$back.fadeIn(function () {
$contain.animate({
marginLeft: "-=" + regWidth + "px"
});
});
}
if (marLeft > -combinedWidth) {
$contain.animate({
marginLeft: 0
});
}
});
Sometimes is better if you create a function to take care of the animation, instead of writting animation code on every event handler (next, back). Also, users won't have to wait for the animation to finish in order to go the nth page/box.
Maybe this will help you:
if (jQuery) {
var $next = $(".next"),
$back = $(".back"),
$box = $(".box"),
regWidth = $box.width(),
$contain = $(".wrap")
len = $box.length;
var combinedWidth = regWidth*len;
$contain.width(combinedWidth);
var currentBox = 0; // Keeps track of current box
var goTo = function(n) {
$contain.animate({
marginLeft: -n*regWidth
}, {
queue: false, // We don't want animations to queue
duration: 600
});
if (n == 0) $back.fadeOut('fast');
else $back.fadeIn('fast');
currentBox = n;
};
$next.click(function(e) {
e.preventDefault();
var go = currentBox + 1;
if (go >= len) go = 0; // Index based, instead of margin based...
goTo(go);
});
$back.click(function(e) {
e.preventDefault();
var go = currentBox - 1;
if (go <= 0) go = 0; //In case back is pressed while fading...
goTo(go);
});
}
Here's an updated version of your jsFiddle: http://jsfiddle.net/victmo/ZQg5V/5/
Cheers!
Use a variable to track if the animation is taking place. Pseudocode:
var animating = false;
function myAnimation() {
if (animating) return;
animating = true;
$(this).animate({what:'ever'}, function() {
animating = false;
});
}
Crude, but it should give you the idea.
Edit: Your current code works fine for me as well, even if I jam out on the button. On firefox.

Categories

Resources