Multi-element jQuery animation issue - javascript

I am trying to loop through an animation selecting multiple elements and moving them as long as the mouse hovers over the parent area. This works well enough, but each time the animation loops through the first element (child) moves faster than the others. ??? JSFiddle Example
HTML:
<div id="menuContent">
<button id="btn1" class="mainButton" left="0"/>
<button id="btn2" class="mainButton" left="0"/>
<button id="btn3" class="mainButton" left="0"/>
</div>
jQuery:
$("#menuContent").hover(function () {
loop();
}
, function () {
stop();
}
);
function stop() {
$(".mainButton").stop();
}
function loop() {
$(".mainButton").stop().animate({ left: "+=20"}, 100, 'linear', function () { loop(); });
}

From the documentation:
complete
A function to call once the animation is complete, called once per matched element.
When you call animate it starts 3 animations. Animation for the first element gets started and finished first. And then its complete gets called and you stop and start all animations, though some of them didn't get completed yet.
Consider this example (Fiddle):
function loop() {
$('.mainButton').stop().animate({
left: '+=1'
}, 1, 'linear', function() {
loop();
});
}
Only one circle will be moving because there is no time gap for others to move.
You can use promises to make it work (Fiddle):
$('#menuContent').hover(function() {
$('.mainButton').data('run', true);
loop();
}, function() {
$('.mainButton').data('run', false);
});
function loop() {
if (!$('.mainButton').data('run')) return;
$('.mainButton').animate({left: '+=10'}, 100, 'linear').promise().done(loop);
}

Danil Speransky is correct. However there is an options argument to the animate function to allow animations to not run in a rigid queue.
`$(".mainButton").animate({ left: "+=20"},{queue: false}, 100, 'linear', function () { loop();});`
Check out the documentation for queue:false here.

You're mileage may vary, but doing these two things seem to help a lot:
First, store the jQuery object for the .mainButton elements:
var $mainButton = $('.mainButton')
Second, Make the left increment more and also increase the delay:
$mainButton.stop().animate(
{ left: "+=1000"},
5000,
'linear',
function() { loop() })
You can toy with the numbers more to see if you get even better performance.
https://jsfiddle.net/wotfLyuo/8/

Your complete handler is called, if the animation for one elements in the jquery collection is finished. So when the first element is finished, you call loop and stop the animation of the other elements. Better use promise and done and save the state of your animation within the collection:
$("#menuContent").hover(function () {
start();
}, function () {
stop();
});
function start() {
$(".mainButton").data('stopped', false);
loop();
}
function stop() {
$(".mainButton").data('stopped', true).stop();
}
function loop() {
var $mainButtons = $(".mainButton").stop();
if(!$mainButtons.data('stopped'))
$mainButtons.animate({ left: "+=20"}, 100, 'linear').promise().done(loop);
}
Here is a working fiddle (https://jsfiddle.net/wotfLyuo/5/)

Related

wrap set time out function around simple slide animation jquery

I have a simple slide down function using jQuery where I'm simply animating an image to slide down. It works great. However I'm trying to reset the animation after it's ran, so it will continue on loop during the duration of the users session. I tried the below, but it didn't work as wanted, slide animation still ran, but not the sought effect of timing out and restarting. Thanks for any thoughts.
$(window).load(function () {
setTimeout(function(){
$("#man").show("slide", {
direction: "up"
}, 2000);
},500);
});
You can call a function after it completes the 2000ms transition.
Updated fiddle: http://jsfiddle.net/CqR9E/392/
$(window).load(function () {
setTimeout( go(),500);
});
function go(){
$("#man").show("slide", {
direction: "up"
}, 2000, function(){
$( "#man:visible" ).removeAttr( "style" ).fadeOut();
go();
});
}

Jquery/Javascript animate issue

Hi I'm having a div which width is increased with jquery. I want when that width reaches 100% to do something
$(function() {
$('.play').click(function() {
$('.loader').animate({
width: "100%"
},1500);
$('.video img').attr('src','css/images/movie-click.jpg')
$(this).hide();
if($('.loader').width()==$('.video img').width()) {
$('.video img').attr('src','css/images/movie.jpg')
}
});
Something is not right in the if statement. if someone can help me. My idea is to check if the with is 100%, and if it is, everything to be back to normal.(play to be showen, width=0%, img attr different.)
Use the complete callback in animate to execute code once the animation is finished:
$('.play').click(function () {
$('.loader').animate({
width: "100%"
}, 1500, function() {
// any code here will run only after the animation is complete
$('.video img').attr('src', 'css/images/movie.jpg');
});
// any code here will run as soon as the animation starts,
// before it's completed
});
The code in the click function is only called once, so your if statement is not being ran continuously as it animates. Instead, simply add that logic to a callback after the .animate() is complete:
$('.loader').animate({
width: "100%"
},1500,swing, function() {
$('.video img').attr('src','css/images/movie.jpg');
});
Something is not right in the if statement
Indeed :-) Your current code just compares the css values returned by the two .width() calls - which will be false of course.
That's not how you wait for an animation. The animation code is asynchronous, your animate() call just starts the animation but returns immediately (and goes on hiding the button and evaluating the condition).
Instead, pass a callback function to animate, it will be executed when the animation has completed:
$(function() {
$('.play').click(function() {
$(this).hide();
var $img = $('.video img');
$img.attr('src','css/images/movie-click.jpg');
$('.loader').animate({
width: "100%"
}, 1500, function() {
// executed after the animation
$img.attr('src','css/images/movie.jpg');
});
});
});

How to add a wait time before hover function is executed

I am working on a nested menu, and when my mouse move over a option, a sublist will show up.
Here is my hover function:
$( ".sublist" ).parent().hover( function () {
$(this).toggleClass("li_hover",300); //use to change the background color
$(this).find(".sublist").toggle("slide", {}, 500); //sub list show / hide
});
Now, I want add a short period before the sublist shows up to prevent the crazy mouse moving from user. Does somebody have a good suggestion on this?
Update:
Thanks for you guys, I did a little bit change on my program, recently it looks like this:
function doSomething_hover (ele) {
ele.toggleClass("li_hover",300);
ele.find(".sublist").toggle("slide", {}, 500);
}
$(function () {
$( ".sublist" ).parent().hover( function () {
setTimeout(doSomething_hover($(this)), 3000);
});
}):
This is weird that setTimeout will not delay anything. but if I change the function call to doSomething_hover (without "()"), the function will delay good. but i can not pass any jquery element to the function, so it still not works, could somebody tell me that how to make doSomething_hover($(this)) work in setTimeout ?
Update 2:
Got the setTimeout work, but it seems not what I want:
What I exactly want is nothing will happen, if the mouse hover on a option less than 0.5sec.
Anyway, here is the code I make setTimeout work:
function doSomething_hover (ele) {
ele.toggleClass("li_hover",300);
ele.find(".sublist").toggle("slide", {}, 500);
}
$(function () {
$( ".sublist" ).parent().hover( function () {
var e = $(this);
setTimeout(function () { doSomething_hover(e); }, 1000);
});
}):
Final Update:
I got this work by using clearTimeout when I move the mouse out.
so the code should be:
$( ".sublist" ).parent().mouseover( function () {
var e = $(this);
this.timer = setTimeout(function () { doSomething_hover(e); }, 500);
});
$( ".sublist" ).parent().mouseout ( function () {
if(this.timer){
clearTimeout(this.timer);
}
if($(this).hasClass("li_hover")){
$(this).toggleClass("li_hover");
}
$(this).find(".sublist").hide("slide", {}, 500);
});
This is the part in the $(document).ready(). Other code will be same as above.
真. Final Update:
So, mouseover and mouseout will lead to a bug sometime, since when I move the mouse to the sublist, the parents' mouseover event will be fire, and hide the sublist.
Problem could be solved by using hover function:
$( ".sublist" ).parent().hover(
function () {
var e = $(this);
this.timer = setTimeout(function () { doSomething_hover(e); }, 500);
},
function () {
if(this.timer){
clearTimeout(this.timer);
}
$(this).find(".sublist").hide("slide", {}, 500);
if($(this).hasClass("li_hover")){
$(this).toggleClass("li_hover",300);
}
}
);
Thanks all
Try this please:
Code
setInterval(doSomthing_hover, 1000);
function doSomthing_hover() {
$(".sublist").parent().hover(function() {
$(this).toggleClass("li_hover", 300); //use to change the background color
$(this).find(".sublist").toggle("slide", {}, 500); //sub list show / hide
});
}​
SetTime vs setInterval
At a fundamental level it's important to understand how JavaScript timers work. Often times they behave unintuitively because of the single thread which they are in. Let's start by examining the three functions to which we have access that can construct and manipulate timers.
var id = setTimeout(fn, delay); - Initiates a single timer which will call the specified function after the delay. The function returns a unique ID with which the timer can be canceled at a later time.
var id = setInterval(fn, delay); - Similar to setTimeout but continually calls the function (with a delay every time) until it is canceled.
clearInterval(id);, clearTimeout(id); - Accepts a timer ID (returned by either of the aforementioned functions) and stops the timer callback from occurring.
In order to understand how the timers work internally there's one important concept that needs to be explored: timer delay is not guaranteed. Since all JavaScript in a browser executes on a single thread asynchronous events (such as mouse clicks and timers) are only run when there's been an opening in the execution.
Further read this: http://ejohn.org/blog/how-javascript-timers-work/
timeout = setTimeout('timeout_trigger()', 3000);
clearTimeout(timeout);
jQuery(document).ready(function () {
//hide a div after 3 seconds
setTimeout( "jQuery('#div').hide();",3000 );
});
refer link
function hover () {
$( ".sublist" ).parent().hover( function () {
$(this).toggleClass("li_hover",300); //use to change the background color
$(this).find(".sublist").toggle("slide", {}, 500); //sub list show / hide
});
}
setTimeout( hover,3000 );
....
You could use .setTimeout
$(".sublist").parent().hover(function() {
setTimeout(function() {
$(this).toggleClass("li_hover", 300); //use to change the background color
$(this).find(".sublist").toggle("slide", {}, 500); //sub list show / hide
}, 1000);
});​

setInterval to loop animation not working

I have a simple fadeIn fadeOut animation, it's basically a blinking arrow. However, it doesn't loop. It just goes once, and it's done. I found an answer here -> How to repeat (loop) Jquery fadein - fadeout - fadein, yet when I try to follow it, mine doesn't work.The script for the animation is
<script type="text/javascript">
$(document).ready(function() {
$('#picOne').fadeIn(1000).delay(3000).fadeOut(1000);
$('#picTwo').delay(5000).fadeIn(1000).delay(3000).fadeOut(1000);
});
</script>
the script given in the answer is
$(function () {
setInterval(function () {
$('#abovelogo').fadeIn(1000).delay(2000).fadeOut(1500).delay(2000).fadeIn(1500);
}, 5000);
});
so I assume the end combination would be
$(document).ready(function() {
setInterval(function () {
$('#picOne').fadeIn(1000).delay(3000).fadeOut(1000);
$('#picTwo').delay(5000).fadeIn(1000).delay(3000).fadeOut(1000);
}, 5000);
});
</script>
Could someone please point out what I'm doing wrong? thanks
Two details :
You have to set the interval to 10000 because your animation run 10s
If you want it to start now, you have to call it one time before executing the interval (the first execution of the interval is after the delay)
--
$(document).ready(function() {
function animate() {
$('#picOne').fadeIn(1000).delay(3000).fadeOut(1000);
$('#picTwo').delay(5000).fadeIn(1000).delay(3000).fadeOut(1000);
}
animate();
setInterval(animate, 10000);
});​
Demonstration here : http://jsfiddle.net/bjhG7/1/
--
Alternative code using callback instead of setInterval (see comments):
$(document).ready(function() {
function animate() {
$('#picOne').fadeIn(1000).delay(3000).fadeOut(1000);
$('#picTwo').delay(5000).fadeIn(1000).delay(3000).fadeOut(1000, animate);
}
animate();
});​
Demonstration here : http://jsfiddle.net/bjhG7/3/
function fadein(){
$('#picOne,#picTwo').animate({'opacity':'1'},1000,fadeout())
}
function fadeout(){
$('#picOne,#picTwo').animate({'opacity':'0'},1000,fadein())
}
fadein()
Take advantage of the callback argument of .fadeOut(). Pass a reference to the function that does the fading as the callback parameter. Choose which image to fade based on a counter:
$(function() {
var imgs = $('#picOne,#picTwo');
var fadeCounter = 0;
(function fadeImg() {
imgs.eq(fadeCounter++ % 2).fadeIn(1000).delay(3000).fadeOut(1000, fadeImg);
})();
});
Demo: http://jsfiddle.net/KFe5h/1
As animation sequences get more complex, I've found using async.js leads to more readable and maintainable code. Use the async.series call.

How do I get jQuery to apply an effect to each item in an .each() loop before it finishes?

I have the following list:
<div id="test">test</div>
<ul>
<li>foo</li>
<li>bar</li>
<li>sta</li>
<li>cko</li>
</ul>
and the following jQuery code:
$(document).ready(function() {
$('li').each(function(index) {
$('#test').text($(this).text());
$('#test').show("drop", { direction: "down" }, 500, function() {
$("#test").delay(1000).hide("puff", {}, 100);
});
});
});
Logically, this should change the contents of the div test to foo, apply the drop and puff effect, change contents to bar, apply the effect, and so on. But when you run it, the entire .each loop finishes up before the effects are started, so the last element cko shows up first and gets animated 4 times.
How can i make each item get the effect and then move on to the next one?
You need to add the first function on the queue as well if you want it to happen in the order of the queue, (using .queue()), like this:
$(function() {
$('li').each(function(index) {
var li = $(this);
$('#test').queue(function(n) {
$(this).text(li.text()); //set the text
n(); //call next function in the queue
}).show("drop", { direction: "down" }, 500, function() {
$(this).delay(1000).hide("puff", {}, 100);
});
});
});​
You can give it a try here. This queues the text setting to happen in sequence with the animations, which seems to be what you're after. Calling the function passed to your queue callback is important, as it's what advances the queue, firing the animation that follows in this case.
The .delay() will have a weird effect the way you have it as well, if you want it on the queue, do it like this:
$(function() {
$('li').each(function(index) {
var li = $(this);
$('#test').queue(function(n) {
$(this).text(li.text());
n();
}).show("drop", { direction: "down" }, 500)
.delay(1000).hide("puff", {}, 100);
});
});​
You can try it here, this will actually pause a second for each element before moving on.
Not tested, but I think you can try:
$(document).ready(function() {
animateItem($('li'), 0);
});
function animateItem(li, i) {
if (li.length > i) {
$('#test').text(li.eq(i).text());
$('#test').show("drop", { direction: "down" }, 500, function() {
$("#test").delay(1000).hide("puff", {}, 100, function() {
animateItem(li, i++);
});
});
}
}

Categories