Execute a Function after all slideDowns/slideUps are completed - javascript

I have this jQuery function which runs jQuery slideUp/slideDown.
There are two slide Options which would get executed at a time.
Here's the code:
updateView: function(){
var falseItems = jQuery(".cjs-milestone-task-container-false");
var trueItems = jQuery(".cjs-milestone-task-container-true");
var linkObject = jQuery("#cjs_milestones_see_all");
if(jQuery(MentoringModelMilestones.milestoneBlock).data("mode") == MentoringModelMilestones.viewStatus){
linkObject.html(linkObject.data("collapsed"));
linkObject.addClass("collapsed");
**falseItems.slideUp();**
MentoringModelMilestones.toggleSeeAll(falseItems.length == 0);
}
else{
linkObject.html(linkObject.data("expanded"));
linkObject.removeClass("collapsed");
**falseItems.slideDown();**
MentoringModelMilestones.toggleSeeAll(trueItems.length == 0);
}
**trueItems.slideDown();**
},
I need to call this function after the slideUp/slideDown are completed:
addMergeTop: function(){
jQuery(MentoringModelMilestones.milestoneBlock).find(".cjs_milestone_container").addClass('merge-top');
jQuery(MentoringModelMilestones.milestoneBlock).find(".cjs_milestone_container:visible").first().removeClass('merge-top');
},
Passing this as a function to slideUp/slideDown doesn't work.

It sounds like you want to chain the calls to slideUp and slideDown:
falseItems.slideUp(400 /* duration */, function() {
trueItems.slideDown(400, function() {
/* perform actions that should be done after both animations complete */
console.log("all animations done");
});
});
You'll need to do something similar for both cases of the if/else.
Note that this will perform one operation after the other. If you want both operations to occur simultaneously (or if you don't want to merge these two calls), then you'll need to do something hackier to maintain shared state. For example:
function slideAll() {
var count = 0;
$("#falseItems").slideUp("slow" /* duration */ , function() {
if (2 === ++count) {
alert("all done");
}
});
$("#trueItems").slideDown("slow", function() {
if (2 === ++count) {
alert("all done");
}
});
}
slideAll();
You don't have to do all of this in one slideAll function, but (however you do it) you do need to keep track of which animations have finished before you execute any code that relies on both.
See the second example in action:
http://jsfiddle.net/EKJGx/

Attach the callback function as a second parameter of your slideUp() or slideDown().
FIDDLE
function doSlide(){
$('.slider').slideUp(1000,function(){
alert('done sliding');
})
}

You can do like this. I am giving only a logic you can modify it to your needs
//place this at top
var falseItemsComplete = trueItems = false;
//modify below things in your code
falseItems.slideUp('fast', function() {
falseItemsComplete = true;//slideup for falseItems completed
checkCompletedStatus();
});
trueItems.slideDown('fast', function() {
trueItemsComplete = true;//slideup for trueItems completed
checkCompletedStatus();
});
function checkCompletedStatus(){
if(falseItemsComplete && trueItemsComplete ){
//both animations complete
alert(1)
}
}

Related

temporarily stopping function jquery

var start = $('#start_img');
start.on('click', function(){
var piano = $('.piano');
piano.each(function(index){
$(this).hide().delay(700 * index).fadeIn(700*index);
start.off('click');
})
});
You can see that I have used the start.off('click') method, to stop the Event Listener from running again once it has been called. But the thing is, I only want the Event listener to be off during the time that the event is running. So that it cannot be called again while the event is still running. But once the event has finished, I want it to be 'callable' again. Does anyone know how t do this?
other way of doing this (doesn't work neither). Can anyone help me here. The other one is now clear.
var start = $('#start_img');
start.on('click', function() {
var q = 0;
var piano = $('.piano');
if (q === 1) {
return; // don't do animations
}
else{
piano.each(function(index) {
q = 1;
$(this).hide()
.delay(700 * index)
.fadeIn(700 * index, function() {
// remove from each instance when animation completes
q = 0
});
});}
});
You could toggle a class on active elements as well and then you can check for that class and not do anything if it exists
start.on('click', function() {
var piano = $('.piano');
if (piano.hasClass('active')) {
return; // don't do animations
}
piano.each(function(index) {
$(this).addClass('active')
.hide()
.delay(700 * index)
.fadeIn(700 * index, function() {
// remove from each instance when animation completes
$(this).removeClass('active')
});
});
});
For only one object, you could use a global variable for this, in my case, I'll be using isRunning:
var start = $('#start_img');
var isRunning = false;
start.on('click', function(){
if (!isRunning){
isRunning = true;
var piano = $('.piano');
piano.each(function(index){
$(this).hide().delay(700 * index).fadeIn(700*index, function(){
isRunning = false;
});
start.off('click');
});
}
});
This way your app shouldn't run the code until isRunning == false, which should happen after fadeIn is completed.
Syntaxes:
.fadeIn([duration] [,complete]);
.fadeIn(options);
.fadeIn([duration] [,easing] [,complete]);
For two or more objects, Charlietfl's answer should work perfectly.

Function when multiple elements have been clicked

I want a to have an animation only when seven elements have been click. Here is the code but it doesn't work:
var animp5 = function () {
var i = 0;
$("#ans1_p5").on('click', function () {
i = i + 1;
$("#ans1_p5").fadeOut(800);
$("#correct1_p5").fadeIn(1000);
});
$("#ans2_p5").on('click', function () {
i = i + 1;
$("#ans2_p5").fadeOut(800);
$("#correct2_p5").fadeIn(1000);
});
$("#ans3_p5").on('click', function () {
i = i + 1;
$("#ans3_p5").fadeOut(800);
$("#correct3_p5").fadeIn(1000);
});
$("#ans5_p5").on('click', function () {
i = i + 1;
$("#ans5_p5").fadeOut(800);
$("#correct4_p5").fadeIn(1000);
});
$("#ans7_p5").on('click', function () {
i = i + 1;
$("#ans7_p5").fadeOut(800);
$("#correct5_p5").fadeIn(1000);
});
$("#ans9_p5").on('click', function () {
i = i + 1;
$("#ans9_p5").fadeOut(800);
$("#correct6_p5").fadeIn(1000);
});
$("#ans10_p5").on('click', function () {
i = i + 1;
$("#ans10_p5").fadeOut(800);
$("#correct7_p5").fadeIn(1000);
});
if (i === 7) {
$("#ans4").fadeOut(800);
$("#ans6").fadeOut(800);
$("#ans8").fadeOut(800);
$("#wrong1_p5").fadeIn(1000);
$("#wrong2_p5").fadeIn(1000);
$("#wrong3_p5").fadeIn(1000);
$("#cor_p5").fadeIn(1000);
}
};
I have tried other solutions (like .data('clicked') or .attr('clicked') but they didn't work either.
You can use observer design pattern in javascript to achieve this the right way.
First create handlers, subscribe and execute functions and then you can subscribe waht ever you like in your case its comparison i===7. execute fade.execute after every click to validate.
Also it's advisable to use class selectors than id selectors in your case. As id selectors will be unmanageable and you will end up with a lot of duplicate code.
But for the sake of your question observer is your way to go.
jsFiddle
function Fade() { // Create Fade handlers
this.handlers = []; // observers
}
Fade.prototype = { // define subscribe and execute
subscribe: function(fn) {
this.handlers.push(fn);
},
execute: function(o, thisObj) {
var scope = thisObj || window;
this.handlers.forEach(function(item) {
item.call(scope, o);
});
}
};
var fade = new Fade();
fade.subscribe(function(){ // pass function you want to subscribe
console.log(i);
if(i===7){
$("#ans4").fadeOut(800);
$("#ans6").fadeOut(800);
$("#ans8").fadeOut(800);
$("#wrong1_p5").fadeIn(1000);
$("#wrong2_p5").fadeIn(1000);
$("#wrong3_p5").fadeIn(1000);
$("#cor_p5").fadeIn(1000);
}
});
var animp5 = (function(){
var i = 0;
$("#ans1_p5").on('click',function(){
i=i+1;
$("#ans1_p5").fadeOut(800);
$("#correct1_p5").fadeIn(1000);
fade.execute(); // execute to check if condition met
});
$("#ans2_p5").on('click',function(){
i=i+1;
$("#ans2_p5").fadeOut(800);
$("#correct2_p5").fadeIn(1000);
fade.execute();
});
$("#ans3_p5").on('click', function(){
i=i+1;
$("#ans3_p5").fadeOut(800);
$("#correct3_p5").fadeIn(1000);
fade.execute();
});
$("#ans5_p5").on('click', function(){
i=i+1;
$("#ans5_p5").fadeOut(800);
$("#correct4_p5").fadeIn(1000);
fade.execute();
});
$("#ans7_p5").on('click', function(){
i=i+1;
$("#ans7_p5").fadeOut(800);
$("#correct5_p5").fadeIn(1000);
fade.execute();
});
$("#ans9_p5").on('click', function(){
i=i+1;
$("#ans9_p5").fadeOut(800);
$("#correct6_p5").fadeIn(1000);
fade.execute();
});
$("#ans10_p5").on('click', function(){
i=i+1;
$("#ans10_p5").fadeOut(800);
$("#correct7_p5").fadeIn(1000);
fade.execute();
});
})();
Thanks for your answers.
As I have not much experience working with jquery I was unable to code your solution but I found a new one that works perfect. I put the "if" inside every click function so each time I click, code checks if the condition has been fulfilled and once this happens run the appropriate code.
Thanks again

Jquery Animations not waiting for callback

I have a Jquery animation that is running the code from its function before the animation is complete. the Page this code is being used at is no where near complete yet but if you want to take a look it's cottageboards.com/orderform
$('#snow').fadeIn(500, "linear", function () {
$('#largeImage').fadeOut(500, function () {
$('#largeImage').attr('src', selectedimg).load(function () {
$('#largeImage').fadeIn(1000, function () {
//Everything below here is running before the above image's fade in is complete
$('#snow').fadeOut(5000);
var selection = 'input[name="' + $(selectionage).data('type') + '_selection"]';
$($('#selected_thumb').val()).attr('src', $($('#selected_thumb').val()).data('regular'));
$(selectionage).attr('src', $(selectionage).data('selected'));
selectedid = '#' + $(selectionage).attr('id');
$('#selected_thumb').val(selectedid);
$('#selected_info').html($(selectionage).data('desc'));
$('#name').html($(selectionage).data('name'));
if ($(selectionage).data('val') === 99) {
$('#upload').show();
$('#displayinfo').hide();
} else {
$(selection).val($(selectionage).data('val'));
$('#upload').hide();
$('#displayinfo').show();
}
$('#next').prop('disabled', false);
});
});
});
});
When rewritten so the load function comes before the src change it works like a charm. Thanks for the help guys!
Working code:
$('#snow').fadeIn(500, "linear", function () {
$('#largeImage').fadeOut(500, function () {
$('#largeImage').unbind().load(function () {
$('#largeImage').fadeIn(1000, function () {
$('#snow').fadeOut(5000);
var selection = 'input[name="' + $(selectionage).data('type') + '_selection"]';
$($('#selected_thumb').val()).attr('src', $($('#selected_thumb').val()).data('regular'));
$(selectionage).attr('src', $(selectionage).data('selected'));
selectedid = '#' + $(selectionage).attr('id');
$('#selected_thumb').val(selectedid);
$('#selected_info').html($(selectionage).data('desc'));
$('#name').html($(selectionage).data('name'));
if ($(selectionage).data('val') === 99) {
$('#upload').show();
$('#displayinfo').hide();
} else {
$(selection).val($(selectionage).data('val'));
$('#upload').hide();
$('#displayinfo').show();
}
$('#next').prop('disabled', false);
});
}).attr('src', selectedimg);
});
});
You are binding the load function to largeimage every time you click. The first click the load function gets called once, the second time, it gets called twice. I suspect everything is getting messed up because you are firing multiple .fadeIns on the same object, and they are running in parallel.
Only call $('#largeImage').load(...) once, not on every click. Of course, you'll have to do something about your captured vars, but that's a different issue. Alternatively, call $('#largeImage').unbind().load(...)
If that's hard to follow, replace this line:
$('#largeImage').attr('src', selectedimg).load(function () {
with:
$('#largeImage').unbind().attr('src', selectedimg).load(function () {
I tested it by putting a break point after this line:
$('#thumbs').delegate('img','click', function() {
and calling $('#largeImage').unbind(); and everything seemed to work, so you can do it that way too.
see this fiddle for example how to use done : http://jsfiddle.net/gcnes8b2/1/
$('span').click(function() {
$('#1').fadeIn({
duration: 1000,
done:function(){
$('#2').fadeOut(1000);
// etc
}
});
});

How to pass variable asyncronously through different scripts

I can't figure out how to do it.
I have two separate scripts. The first one generates an interval (or a timeout) to run a specified function every x seconds, i.e. reload the page.
The other script contains actions for a button to control (pause/play) this interval.
The pitfall here is that both sides must be asyncronous (both run when the document is loaded).
How could I properly use the interval within the second script?
Here's the jsFiddle: http://jsfiddle.net/hm2d6d6L/4/
And here's the code for a quick view:
var interval;
// main script
(function($){
$(function(){
var reload = function() {
console.log('reloading...');
};
// Create interval here to run reload() every time
});
})(jQuery);
// Another script, available for specific users
(function($){
$(function(){
var $playerButton = $('body').find('button.player'),
$icon = $playerButton.children('i');
buttonAction = function(e){
e.preventDefault();
if ($(this).hasClass('playing')) {
// Pause/clear interval here
$(this).removeClass('playing').addClass('paused');
$icon.removeClass('glyphicon-pause').addClass('glyphicon-play');
}
else {
// Play/recreate interval here
$(this).removeClass('paused').addClass('playing');
$icon.removeClass('glyphicon-play').addClass('glyphicon-pause');
}
},
buttonInit = function() {
$playerButton.on('click', buttonAction);
};
buttonInit();
});
})(jQuery);
You could just create a simple event bus. This is pretty easy to create with jQuery, since you already have it in there:
// somewhere globally accessible (script 1 works fine)
var bus = $({});
// script 1
setInterval(function() {
reload();
bus.trigger('reload');
}, 1000);
// script 2
bus.on('reload', function() {
// there was a reload in script 1, yay!
});
I've found a solution. I'm sure it's not the best one, but it works.
As you pointed out, I eventually needed at least one global variable to act as a join between both scripts, and the use of a closure to overcome asyncronous issues. Note that I manipulate the button within reload, just to remark that sometimes it's not as easy as moving code outside in the global namespace:
Check it out here in jsFiddle: yay! this works!
And here's the code:
var intervalControl;
// main script
(function($){
$(function(){
var $playerButton = $('body').find('button.player'),
reload = function() {
console.log('reloading...');
$playerButton.css('top', parseInt($playerButton.css('top')) + 1);
};
var interval = function(callback) {
var timer,
playing = false;
this.play = function() {
if (! playing) {
timer = setInterval(callback, 2000);
playing = true;
}
};
this.pause = function() {
if (playing) {
clearInterval(timer);
playing = false;
}
};
this.play();
return this;
};
intervalControl = function() {
var timer = interval(reload);
return {
play: function() {
timer.play();
},
pause: function(){
timer.pause();
}
}
}
});
})(jQuery);
// Another script, available for specific users
(function($){
$(function(){
var $playerButton = $('body').find('button.player'),
$icon = $playerButton.children('i'),
interval;
buttonAction = function(e){
e.preventDefault();
if ($(this).hasClass('playing')) {
interval.pause();
$(this).removeClass('playing').addClass('paused');
$icon.removeClass('glyphicon-pause').addClass('glyphicon-play');
}
else {
interval.play();
$(this).removeClass('paused').addClass('playing');
$icon.removeClass('glyphicon-play').addClass('glyphicon-pause');
}
},
buttonInit = function() {
$playerButton.on('click', buttonAction);
interval = intervalControl();
};
buttonInit();
});
})(jQuery);
Any better suggestion is most welcome.

Javascript - Wait a function to finish

I need to implement a confirm box replacement by using jquery dialog. I have a calling function like this
function callingFunc() {
var a = confirmJquery("text", 300, 300, "ok", "cancel");
if (a == true) {
.....
}
else {
....
}
}
This is the confirmJquery function
function confirmJquery(msg, width, height, txtOk, txtCancel) {
var div = document.createElement('div');
div.className = "confirmJquery";
var span = document.createElement('span');
$(span).html(msg);
div.appendChild(span);
var buttonOk = document.createElement('button');
buttonOk.className = 'buttonStyleBigger';
$(buttonOk).html(txtOk);
var buttonCancel = document.createElement('button');
buttonCancel.className = 'buttonStyleBigger';
$(buttonCancel).html(txtCancel);
var divBottom = document.createElement('div');
divBottom.className = 'dialogAction';
divBottom.appendChild(buttonOk);
divBottom.appendChild(buttonCancel);
div.appendChild(divBottom);
var dialog = window.parent.$(div).appendTo(window.parent.document.body);
// open the dialog
dialog.dialog({
height: height,
width: width,
resizable: false,
// add a close listener to prevent adding multiple divs to the document
close: function(event, ui) {
// remove div with all data and events
dialog.remove();
},
modal: true
});
$(buttonOk).bind('click', function(){
return true;
});
$(buttonCancel).bind('click', function() {
return false;
});
}
The problem is, the confirmJquery function always finish before the button (Ok or Cancel) is pressed; hence, there is no value in the calling function. I need to make the confirmJquery waits until user press the button and then function finish and the rest of the calling function continues. How can i do that ?
I need to update more details: I already tried the call back function way. It works perfectly. But, life is not easy like that. This is a very big, old, messy system. Doing that requires me to re-write lot lot of functions, so i need to create a function that act exactly like the confirm function of javascript
Since your function is going to be asynchronous, you need to use a callback. Something like this:
function myCallback(result)
{
if (result) {
// OK
} else {
// Cancel
}
}
function confirmJquery(msg, width, height, txtOk, txtCancel, callback) {
...
$(buttonOk).bind('click', function(){
callback(true);
});
$(buttonCancel).bind('click', function() {
callback(false);
});
}
and
confirmJquery(msg, width, height, txtOk, txtCancel, myCallback);
Move the rest of the function inside another function, and execute that second function at the end of the confirmJquery function.
function firstfunction(){
// Do Some Stuff
secondfunction();
}
first, to avoid a long list of arguments on the receiving side, you can use an object of arguments instead. then send over a callback to confirmJquery
function callingFunc() {
var a = confirmJquery({
msg:"text",
width:300,
height:300,
txtOk:"ok",
txtCancel:"cancel"
},function(ok){
if(ok){
...
} else {
...
}
});
}
function confirmJquery(options,callback) {
//options.msg
//options.width
...
$(buttonOk).bind('click', function(){
callback(true);
});
$(buttonCancel).bind('click', function() {
callback(false);
});
}
yes, alexander is right, just reorganize code, one for dialog, one for function based on a flag/msg. just like mvc pattern.

Categories