I have this function, that adds 31px to my style bottom on ul.
It works fine, but if I click fast, it no longer adds 31px, but some weird number like 17.423px. I guess it's because the animation isn't done, and thereby by clicking fast it adds 31px to the current px amount. My thought is that I need to insert a stop(). But where?
$(function () {
$('.topTenTwocolumns .topTenTwocolumnsWrap .down').click(function () {
if ($("ul").css("bottom") !== '2728px') {
$(this).parents('.topTenTwocolumnsWrap').children('ul').stop().animate({
bottom: '+=31'
}, 200);
}
});
});
The animations are queued, but it might be that the values to animate are calculated before the animation is queued, and as you are using a relative value it would calculate the animation from it current position.
You already have a stop call in the code. I don't know if you added that afterwards, otherwise that could also explain the values. If you stop the animation halfways, the bottom value will naturally be changed halfways.
You could keep the current target value in a variable so that you can specify an absolute value for the animation:
$(function () {
var current = 0;
$('.topTenTwocolumns .topTenTwocolumnsWrap .down').click(function () {
if (current < 2728) {
current += 31;
$(this).parents('.topTenTwocolumnsWrap').children('ul').stop().animate({
bottom: current + 'px'
}, 200);
}
});
});
This way you can keep the stop call so that the new animation replaces the previous instead of being queued.
I would probably try to prevent the function from firing if one is already in flight. Something like this might work:
$(function () {
$('.topTenTwocolumns .topTenTwocolumnsWrap .down').click(function () {
if ($(this).data('in_flight') === true){
return false;
} else {
if ($("ul").css("bottom") !== '2728px') {
$(this).data('in_flight', true);
$(this).parents('.topTenTwocolumnsWrap').children('ul').stop().animate({
bottom: '+=31'
}, 200);
$(this).data('in_flight', false);
}
}
});
});
A more complicated approach would be to count the clicks and queue them (triggering the next one after the current running function completes). You could use similar code to do this.
Related
I am currently able to scroll down a page with the help of CasperJS. I saw this article on how to scroll infinite pages based on the visibility of certain attributes. However I don't want to base it on the visibility of an element but was wondering if there is a way to set a timer on how long it should stay scrolling down before exiting out. How would be able to such thing?
//function to scroll
function tryAndScroll(casper) {
casper.page.scrollPosition = {
top: casper.page.scrollPosition["top"] + 40000,
left: 0
};
}
/**
* Everything starts here!
* I use the mobile version of facebook as the DOM is waaay simpler to scrape.
*/
casper.start('https://www.somesite.com', function() {
});
casper.then(function() {
tryAndScroll(this);
});
casper.then(function() {;
this.exit();
});
casper.run();
Here's a simple way:
function tryAndScroll(casper) {
casper.page.scrollPosition = {
top: casper.page.scrollPosition["top"] + 300,
left: 0
};
}
casper.start(url).then(function() {
var self = this;
var intervalId = setInterval(function(){
tryAndScroll(self);
}, 100); // retry interval
self.wait(10000 /* infinite scroll timeout */, function(){
clearInterval(intervalId);
});
}).run();
Since setInterval() is not a CasperJS step function, this essentially break out of the control flow of CasperJS. The wait() is necessary so that CasperJS doesn't execute something else during the scroll.
Also, you can't use a scroll distance of 40000 pixels. This is too big and PhantomJS won't be able to take a screenshot.
I am using the following code to animate the disappearing of a textarea once the control is out of focus and the textarea is empty.
$(this).blur(function ()
{
var value = $(this).val().trim();
if (value == "")
{
// empty; make it disappear
$(this).animate({
width: 0,
height: 0
}, 1000,'linear',
function ()
{$(this).parent().css("display", "none")});
}
});
The box disappears without animation. But if i run the following code the animation is still there:
$(this).blur(function ()
{
var value = $(this).val().trim();
if (value == "")
{
// empty; make it disappear
$(this).animate({
width: 0,
height: 0
}, 1000);
}
});
I am not sure why the display:none code is executing before the completion of the animation.
JSFiddle: https://jsfiddle.net/7pbuxtkz/
Give this a try. It utilizes the promise() and done() instead of using it directly from the callback itself. Although your original code works for me, it's worth trying. Promise() will ensure that all of the animations are done.
$('.notesArea').each(function () {
$(this).blur(function () {
var value = $(this).val().trim();
if (value == "") {
// empty; make it disappear
$(this).animate({
width: 0,
height: 0
}, 1000).promise().done(function () {
$(this).parent().css("visibility", "hidden")
});
}
});
});
Let me know how it goes and we'll go from there. As I mentioned before, if this doesn't work, then I feel it's something taking place from your CSS. If that's the case, I'll poke around and see what I can find out for you.
More information on promise(): https://api.jquery.com/promise/
I know you said you're using multiple CSS files, which is why you didn't post it here. I would go through those CSS files and see if there is anything that's setting the transition-duration on it to something lower than what you're setting your animation's duration to.
I have a function that hides and shows divs on scroll based on pageY position, but I also need the ability to have it automatically hide and show divs in order(only the ones with children), sort of like a fake animated Gif, looping forever.
I tried this:
function autoPlay() {
$('.conP').each(function(){
if ($(this).children().length > 0) {
setInterval(function(){
$(this).show().delay('100').hide();
},300);
}
});
}
which is not returning any errors, but it's not hiding or showing any of the divs with class="conP".
Any suggestions as to what I'm doing wrong/how I could improve this?
try this -
function autoPlay() {
$('.conP').each(function(){
if ($(this).children().length > 0) {
var $that = $(this);
setInterval(function(){
$that.show().delay('100').hide();
},300);
}
});
}
You have an incorrect reference to this in your setInterval closure. Refer to "How this works" in JavaScript Garden.
In your case you should save the reference to this in a variable:
$('.conP').each(function() {
var $element = $(this);
setInterval(function () {
$(element).show().delay('100').hide();
}, 300);
});
Or, better use the first argument passed to each, which is equal to $(this) in this case.
Not sure it's a great idea to run intervals inside loops, but I'm guessing the issue is scope inside the interval function :
function autoPlay() {
$('.conP').each(function(i, elem){
if ( $(elem).children().length ) {
setInterval(function(){
$(elem).show().delay(100).hide();
},300);
}
});
}
I really appreciate all the help guys, I seem to have figured out the animation part:
setInterval( function() {
autoPlay();
},120);
function autoPlay() {
var backImg = $('#outterLax div:first');
backImg.hide();
backImg.remove();
$('#outterLax').append(backImg);
backImg.show();
}
By hiding whichever div is first, and removing it from-then appending it back into-the containing div, and showing the new first div, it animates quite nicely!
I'm am attempting to build a homepage that has animations. I am having hard time controlling my animations though. All I need is to hide elements, and then show elements after a certain time. Loop through that sequence, and pause and show all elements when the someone hovers over the box. Example simple animation.
I have a long way to go. At first I tried using the .css() visibility property, now I'm using .show() and .hide().
I need a way to loop through my animations. I attempt to add another
setTimeout(clear1(), 3000);
to the end of my box1 function, but that wouldn't work for some reason.
I need a way to on a user hover over #box1, that all animations stop. I have tried using .clearQueue, but I couldn't get that to work.
First of all, set to your css:
.box {display: none;}
SHOW ALL BOXES ON HOVER See Demo
This will show all boxes on hover and then continue the animation from where it stopped (will hide the boxes that hadn't shown up during the animation). I think that is what you are after.
var index = 0; // To keep track of the last div showed during animation
var time_of_delay = 1000; // Set the time of delay
// Start the animation
$(document).ready(function () {
box1(time_of_delay);
});
// The hover states
$("#box1_1").hover(
function() {
box1(0);
}, function() {
box1(time_of_delay);
});
// The animation function
function box1 (delay_time) {
var time=delay_time;
if(time>0) {
$(".box").slice(index).each(function() {
$(this).hide().delay(time).show(0);
time=time+time_of_delay;
});
index=0;
} else {
$(".box:visible").each(function() {
index++;
});
$(".box").stop(true).show(0);
}
}
PAUSE ON HOVER See Demo
This will only pause the animation and continue from where it stopped.
var time_of_delay = 1000; // Set the time of delay
// Start the animation
$(document).ready(function () {
box1(time_of_delay);
});
// The hover states
$("#box1_1").hover(
function() {
box1(0);
}, function() {
box1(time_of_delay);
});
// The animation function
function box1 (delay_time) {
var time=delay_time;
if(time>0) {
$(".box:hidden").each(function() {
$(this).delay(time).show(0);
time=time+time_of_delay;
});
} else {
$(".box").stop(true);
}
}
I used setTimeout and clearTimeout and periodically call a function that increments (and resets) the box to display. Since I assign setTimout to boxt, I am able to call clearTimeout(boxt) on box1's hover event so that I can stop specifically that loop. Here's my jsfiddle. It might not be the exact effect you're trying to achieve, but it should be the right functionality and be easily adaptable with a few tweaks. Let me know if this works for you and if you have any questions about how it works :)
LIVE DEMO
var $box = $('#box1').find('.box'),
boxN = $box.length,
c = 0,
intv;
$box.eq(c).show(); // Show initially the '0' indexed .box (first one)
function loop(){
intv = setInterval(function(){
$box.eq(++c%boxN).fadeTo(400,1).siblings().fadeTo(400,0);
},1000);
}
loop(); // Start your loop
$('#box1').on('mouseenter mouseleave', function( e ){
return e.type=='mouseenter' ? (clearInterval(intv))($box.fadeTo(400,1)) : loop();
});
Where ++c%boxN will take care to loop your animation using the Modulo % (reminder) operator inside a setInterval. Than all you need to do is to register a mouseenter and mouseleave on the parent element to:
clear the Interval on mouseenter + fade all your elements
restart your loop function on mouseleave.
Here's one way to do it:
// hide all of the boxes
$('.box').hide();
// reference to each box, the current box in this list and a flag to stop the animation
var divs = box1.getElementsByClassName('box');
var i = 0;
var run = true;
// this will animate each box one after the other
function fade(){
if(i < divs.length && run){
$(divs[i++]).fadeIn(500, function(){
setTimeout(fade, 1000);
});
}
};
fade();
// stop the above function from running when the mouse enters `box1`
$('#box1').on('mouseenter', function(){console.log('enter');
run = false;
});
// start the function again from where we stopped it when the mouse leaves `box1`
$('#box1').on('mouseleave', function(){console.log('leave');
run = true;
fade();
});
Demo: http://jsfiddle.net/louisbros/dKcn5/
I have this code which animates between divs sliding out. If an item is clicked, it's relevant content slides out. If another item is clicked, the current content slides back in and the new content slides out.
However,
var lastClicked = null;
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).toggle(function() {
if (lastClicked && lastClicked != this) {
// animate it back
$(lastClicked).trigger('click');
}
lastClicked = this;
$('.each-brew-content.'+animCls).show().animate({ left: '0' }, 1000).css('position','inherit');
}, function() {
$('.each-brew-content.'+animCls)
.animate({ left: '-33.3333%' }, 1000, function() { $(this).hide()}) // hide the element in the animation on-complete callback
.css('position','relative');
});
})(animateClasses[i]); // self calling anonymous function
}
However, the content sliding out once the already open content slides back is sliding out too quickly - it needs to wait until the content has fully slided back in before it slides out. Is this possible?
Here's a link to what I'm currently working on to get an idea (http://goo.gl/s8Tl6).
Cheers in advance,
R
Here's my take on it as a drop-in replacement with no markup changes. You want one of three things to happen when a menu item is clicked:
if the clicked item is currently showing, hide it
if something else is showing, hide it, then show the current item's content
if nothing is showing, show the current item's content
var lastClicked = null;
// here lastClicked points to the currently visible content
var animateClasses = ['ale', 'bramling', 'bullet', 'miami-weisse'];
for (var i=0; i<animateClasses.length; i++) {
(function(animCls) {
$('.each-brew.'+animCls).click(function(event){
if(lastClicked && lastClicked == animCls){
// if the lastClicked is `this` then just hide the content
$('.each-brew-content.'+animCls).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
}).css('position','relative');
lastClicked = null;
}else{
if(lastClicked){
// if something else is lastClicked, hide it,
//then trigger a click on the new target
$('.each-brew-content.'+lastClicked).animate(
{ left: '-33.3333%' }, 1000,
function() {
$(this).hide();
$(event.target).trigger('click');
}).css('position','relative');
lastClicked = null;
}else{
// if there is no currently visible div,
// show our content
$('.each-brew-content.'+animCls).show()
.animate({ left: '0' }, 1000)
.css('position','relative');
lastClicked = animCls;
}
}
});
})(animateClasses[i]); // self calling anonymous function
}
Well, I'm pretty sure there are other more easy possibilities and I didn't have much time but here is a working jsfiddle: http://jsfiddle.net/uaNKz/
Basicly you use the callback function to wait until the animation is complete. In this special case it's the complete: function(){...}
$("document").ready(function(){
$('#ale').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#alecont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
$('#bramling').click(function(){
if ($('div').hasClass('toggled')){
$('.toggled').animate({ width: "toggle" }, { duration:250, complete: function(){
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');}
}).removeClass('toggled');
}else{
$('#bramcont').animate({ width: "toggle" }, { duration:250 }).addClass('toggled');
}
});
});
I give a toggled class if a div is expanded. Since the animation on your page seems to be pretty much broken I think this would be a better way to do this. But remember: my code isn't really good. Just fast and it can be refactored. It's working tho..
Rather than using toggles, bind an on "click" handler to your ".each-brew" divs. In the handler, first hide content divs and then show the appropriate content when that animation completes. You can do that with either a promise or a callback. Something like this...
$(".each-brew").on("click", function (event) {
$(".each-brew-content").show().animate({ left: "0" }, 1000, function() {
// Get the brew name from the class list.
// This assumes that the brew is the second class in the list, as in your markup.
var brew = event.currentTarget.className.split(/\s+/)[1];
$(".each-brew-content." + brew).animate({ left: "-33.3333%" }, 1000, function() { $(this).hide(); });
});
});
I think an event and observer would do the trick for you.
set up the callback function on completion of your animation to fire an event.
the listener would first listen for any animation event and after that event is triggered listen for the completion event. when the completion event is fired execute the initial animation event.run method (or whatever you would want to call it)
Within the listener
on newanimationeventtriger(new_anim) wait for x seconds (to eliminate infinite loop poss) while if this lastevent triggers done == true{
new_anim.run();
}