I have some jQuery inside javascript functions that changes text on a page and fades it in and out at specific time intervals. I want the functions to run in order one after the other, after each function finishes doing its effects.
dialogueExchange1();
dialogueExchange2();
dialogueExchange3();
function dialogueExchange1() {
$('.text-area1').text("hey");
$('.text-area1').delay(1000).showDialogue(800, 3000).prepareDialogue(800, "hey, are you awake?");
}
function dialogueExchange2() {
$('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up").delay(900);
$('.text-area2').text("...");
$('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
}
function dialogueExchange3() {
$('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "let's go").delay(900);
$('.text-area2').text("not yet");
$('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
}
The showDialogue
and prepareDialogue
are methods I created that delay and fade text in and out. That is working fine. Basically, I'm just trying to get the text to change in the text area selectors after a specific time. What is currently happening is that all the functions are being run at the same time, thus firing the text changing effects all at the same time. I want dialogueExchange1
to do its effects then when it is done, for dialogueExchange2
to do its effects and then when its done, etc etc.
I have tried messing around with queues, timeouts and callbacks via the solutions below, but I haven't gotten it to do exactly what I want:
how to avoid callback chains?
How do I chain or queue custom functions using JQuery?
I had this working in the past and doing what I wanted by just having all the text changing methods chained together in one line of code but it just looks bad. Having it broken up in functions and running them in order would make it more organized and helpful to keep track of the text changes and delay times. Thanks!
EDIT: showDialogue
and prepareDialogue
functions as requested
$.fn.showDialogue = function(fadeInTime, showTextTime) {
this.fadeIn(fadeInTime).delay(showTextTime);
return this;
};
$.fn.prepareDialogue = function(fadeOutTime, dialogue) {
this.fadeOut(fadeOutTime, function() {
$(this).html(dialogue);
});
return this;
};
SOLUTION EDIT2: Thanks for the responses everyone and to whoughton for first suggesting the use of promise()
. This is my solution at the moment, but I'm sure I'm going to refactor it down the road and change it now that I have seen Shaunak's answer.
dialogueExchange1();
function dialogueExchange1() {
$('.text-area1').text("hey");
$('.text-area1').delay(1000).showDialogue(800, 3000).prepareDialogue(800, "hey, are you awake?");
$('.text-area1, .text-area2, .text-area3').promise().done(function() {
dialogueExchange2();
});
}
function dialogueExchange2() {
$('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up");
$('.text-area3').text("...");
$('.text-area3').delay(1800).showDialogue(800, 1500).fadeOut(800);
$('.text-area1, .text-area2, .text-area3').promise().done(function() {
dialogueExchange3();
});
}
function dialogueExchange3() {
$('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "come on let's go");
$('.text-area2').text("hold on");
$('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
}
This way it gives me a lot of flexibility in refining delay times to reflect and mimic a conversation. The next function only runs when the effects within a function are finished, as made possible by promise()
. Here is a jsFiddle link if you want to see it in action.
Here is a a way to do this with promises as @whoughton pointed out:
Solution using .promise() ( jsfiddle )
Html:
<div class="hasEffects effect1"> Div 1</div>
<div class="hasEffects effect2"> Div 2</div>
<div class="hasEffects effect3"> Div 3</div>
<button id="animate">animate</button>
Jquery:
effects1 = function(){
$(".effect1").effect("shake", {}, 1000);
return $(".effect1");
// or you can return $(".hasEffects"); if you are running effects on multiple elements
};
effects2 = function(){
$(".effect2").effect("shake", {}, 1000);
return $(".effect2");
};
effects3 = function(){
$(".effect3").effect("shake", {}, 1000);
return $(".effect3");
};
$("#animate").click(function(){
runAnimations([effects1,effects2,effects3]);
});
runAnimations = function(functionArray) {
//extract the first function
var func = functionArray.splice(0, 1);
//run it. and wait till its finished
func[0]().promise().done(function() {
//then call run animations again on remaining array
if (functionArray.length > 0) runAnimations(functionArray);
});
}
Here is the jsfiddle
How this works
You pass an array of all the functions you need to chain in the runAnimation function which is run recursively until all functions are completed. They way it halts the execution of next function before animation in previous one is completed is using .promise()
feature of jquery.
Everytime the runAnimation()
runs, it extracts the first function in the array and runs it, and after .proimise().done()
is executed for that function it call runAnimation()
again with the remaining array.
The real trick is in understanding how .promise() works. It waits for all animations running on all the selectors passed to it. So in the individual effects function you can run effects on as many elements as you want. As long as you return correct selector you should be good.
For example, suppose in all thee effects you wanted to run 3 different effects on all 3 divs. Thats fine, just return $("hasEffects")
from the function and it will work. :)
In your case
In your particular case here is how you can use this example. Add a grouping class on all your elements for example class="hasEffects"
. And return a jquery selector for them from your animation functions. Then chain them using runAnimation. Here is how it should look:
function dialogueExchange1() {
$('.text-area1').text("hey");
$('.text-area1').delay(1000).showDialogue(800, 3000).prepareDialogue(800, "hey, are you awake?");
return $(".hasEffects");
}
function dialogueExchange2() {
$('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up").delay(900);
$('.text-area2').text("...");
$('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
return $(".hasEffects");
}
function dialogueExchange3() {
$('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "let's go").delay(900);
$('.text-area2').text("not yet");
$('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
return $(".hasEffects");
}
runAnimations([dialogueExchange1,dialogueExchange2,dialogueExchange3]);
runAnimations = function(functionArray){
var func = functionArray.splice(0,1);
func[0]().promise().done(function(){
if(functionArray.length > 0 ) runAnimations(functionArray);
});
}
I would recommend using a system of promises, jQuery has its own implementation:
http://api.jquery.com/promise/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With