Run function only after another function is complete - javascript

I have been trauling the web to find an answer to this, but everything I find seems to fail.
In a function I want to disable a href link. Once this function is complete (including animations) I want to re-enable the link.
I currently have the following:
function prev() {
$prevId = $('.active').prev('.slide-item').attr('id');
if ($prevId) {
$('#prev').bind('click', false);
var id = '#'+$prevId;
$('#thumb-list li a.current').removeClass('current');
var thumb = '#thumb-list li a'+id;
$(thumb).addClass('current');
$('.active').transition({ left: offCanvas }, 300, function() { $(this).hide(); }).removeClass('active');
setTimeout(function() {
$(id).addClass('active').css({ left: -pageWidth }).show().transition({ left: 0 }, 300).css("-webkit-transform", "translate3d(0px,0px,0px)"):
}, 30);
prevFinished();
} else {
$('.active').css("-webkit-transform", "translate3d(0px,0px,0px)");
id = '#'+$('.active').attr('id');
}
$prevId = $(id).prev('.slide-item').attr('id');
$nextId = $(id).next('.slide-item').attr('id');
prevNextCheck();
}
function prevFinished() {
$('#prev').unbind('click', false);
}
But it doesn't work at all.
If anyone could point me in the right direction that would be great.
P.S I tried using callbacks, but unless I am doing something terribly wrong it failed every time.

As stated in the previous answer you could use jQuery's Deferred and Promises. Depending on when exactly you'd want your functions to be executed it'd look something like this:
var deferred = new $.Deferred();
var promise = deferred.promise();
$('.someClass').transition({ left: 0 }, 300, function(e) {
// .. maybe do some other stuff or maybe not
deferred.resolve();
});
promise.then(someFunction);
function someFunction() {
// this runs after your transition has finished
}
You can of course chain multiple then together if you wanted.
Additionally if you have multiple functions that you need to wait for to finish you could also use $.when and wait for all of them to finish.
$.when(promise1,promise2).then(someFunction);

There are a few methods of accomplishing this.
Solution one is to use callbacks. Your animation sequences look overly complex, but it appears you are making a rotation animation for multiple objects, and I don't know the requirements of it. But, one thing I can say is that your callbacks are only encapsulating a small amount of functionality from the looks of it. Widen it out to include EVERY action that is expected to occur after the animation.
Solution two is to use deferred objects and promises. I'm not the best person to ask for a working example of how this works, but the functionality fits your requirement.
Both of these routes are documents jquery features that just need to be put in place correctly.

Related

RaphaelJS animate does not work when called from an outside method

As a desktop developer I am very new to Javascript, so I often run into things that puzzle me about the language. I was working with click events on RaphaelJS shapes, and initially I was setting the state and animation of the object in a private method:
innershape.node.onclick = function () {
if (scope.state === 0) {
_setState(1);
} else {
_setState(0);
}
};
function _setState(state) {
scope.state = state;
if (scope.state === 0) {
innershape.animate({ fill: "#00FF19" }, 500);
} else {
innershape.animate({ fill: "#C05219" }, 500);
}
}
This was functioning as expected. I then decided to add an outside function that would loop through all the objects and de-select (and therefore reverse-animate) all the other shapes. The result may be seen in this jsfiddle: http://jsfiddle.net/txj4zasn/4/
The function is called properly, and the animate() function is apparently executed, but the visible animation never appears, and the color never changes. I suspect that this is something very basic to Javascript that I just don't understand. Can someone explain to me why this is happening?
Its not really very clear what you want to achieve (beyond getting the animation to work), so my initial solution I think isn't good, but I will expand on that.
The problem looks a bit like you are trying to combine two different elements, functional scope and object variables.
A quick solution would be to include...
this.id = 1;
var id = this.id; // so id now a closure to the later function
as updateSelected(id); the id here, is inside another function, so we can't use 'this.id'. But then later you are checking against z[i].id so you need that to be defined also.
jsfiddle
This all feels a bit clunky though, prone to error, and is quite hard to read. So the first question is do you need objects ? You could store information in the "data" part of a Raph element, which already is an object.
Here is an example of how I would write it, I appreciate this may not be suitable as it may be part of a bigger project which needs other elements in an object, but it may give some idea.
function updateSelected( el ) {
if( el.data('innerstate') == 1 ) {
el.animate({ fill: "#00FF19" }, 500);
el.data('innerstate',0)
} else {
el.animate({ fill: "#C05219" }, 500);
el.data('innerstate',1);
}
}
function addElement() {
var innershape = paper.rect(100,100,100, 100);
innershape.attr({fill: "#00FF19" });
innershape.data('innerstate', 0);
innershape.click( function () {
updateSelected( innershape )
} );
};
addElement();
This code I can pretty much read instantly and know how and if it will work.
jsfiddle
jsfiddle showing it combined with more than one element, or jsfiddle thats a bit more compact

SnapSVG animation callback on set of elements not firing

See JSfiddle!
I am wanting to animate a set of elements and execute a callback when finished like so:
s.selectAll('.active').animate( {
transform: matrix
},
300,
mina.linear,
function() {
//callback doesnt fire
alert('callback')
}
)
The elements are animated correctly but the callback isnt executed.
However, when I apply the animation to a group of elements, the callback is fired:
group.animate( {
transform: matrix
},
300,
mina.linear,
function() {
alert('callback')
}
)
.. But I don't want to put my selected elements in a group as this would cause more complications in other places.
Is it possible to animate a set of elements that I got via a .select() or .selectAll() while being able to fire the callback?
Thanks a lot in advance!
Edit: For future readers, you can animate a set of elements by using forEach and counting if all elements are done animating:
function hideToPoint(elements, x, y, callback) {
var finished = 0;
elements.forEach(function(e) {
e.animate( {
//do stuff
},
300,
mina.linear,
function () {
finished++;
if (finished == elements.length) {
callback();
}
}
)
})
}
I'm going to have a stab at answering a couple of problems, even though I'm not sure if related to the callback. Its hard to tell if its just the example code or not without a proper test like a jsfiddle.
However, there are at least 2 problems in the code above.
Creating a matrix is with
new Snap.Matrix(); // as opposed to Snap.matrix()
Also
elements.animate()
The problem here is that animate acts on one element (edit: looks like it can work on elements within a set, but not the callback as example here, edit2: callbacks on sets may now be supported), not multiple elements as such (you can sometimes apply somethings to a set which deals with them individually, but as far as I'm aware, thats not the case with animate).
So you either want to do a
elements.forEach( function(el) { el.animate({blah: value}, 2000, mina.linear.callback )
});
or if its an svg group (as opposed to a set), the original code would possibly work (but I would call it 'myGroup' or something instead of 'elements' for code readability and guessing what it contains)
fiddle (have included a different animation using snap animation string)

Jquery DOM events called after events following it are executed

I am facing a very weird issue in chrome.
My code is
$('#import').text("Importing...");
$('#import img').removeClass("hidden");
Server.postService("tests", row_datas, function(data) {
// some stuff here
});
The text and the hidden class are being removed after the post action has been executed.
The code is working fine in firefox.
The only thing needed was
Server.async = true
before the server call.
Asynchronous functions fire in order of appearance but return in order of when they finish. It would be odd for those simple text and removeClass methods to return more slowly than whatever you're doing with your Server object, but I suppose it's possible. If you need the first two lines to happen before the postService, you might try jQuery's deferred.promise. Here's a fiddle demonstrating the potential, and some code to inspect:
function firstThing (){
var dfd = new jQuery.Deferred();
$('#import').text("Importing...");
$('#import img').removeClass("hidden");
dfd.resolve();
}
$.when( firstThing() ).then(
function() {
Server.postService("tests", row_datas, function(data) {
// some stuff here
});
}
)
As a side note, the logic of your code is problematic in that by setting the text of #import, whatever img had the hidden class won't be there anymore, but that might be beside the point.
Update
Noticing your response to my comment asking about your use of ajax, I would suggest you read about the async option and see how what you're doing might or might not be blocking events.
I would also recommend reading about jQuery ajax callbacks, particularly error, success, and complete (now, with jQuery 1.8+, fail, done, and always).

How do you make animations not fire unless previous is done (withtout using callback)

I have a function called:
this.makeStuffHappen()
Which makes different animations according to a switch statement and a couple of variables. Is it possible to make a function not fire unless all previous are done resolving.
I want to write something like
$('button').click(function(){
a.makeStuffHappen();
b.makeStuffHappen();
c.makeStuffHappen();
d.makeStuffHappen();
});
And then it should only run b after a is done, c after b is done and d after c is done. Is it only possible to make animations wait for the previous by chaining them? Is it possible to chain any function?
Edit: makeStuffHappen() is animating different elements so a.makeStuffHappen() will animate a different element than b.makeStuffHappen and so on.
Thanks.
The easiest (or at least most flexible) way would be using deferred objects [docs]. For that you have to return a deferred object from each function, for example:
a.makeStuffHappen = function() {
elementA.animate(...);
elementB.animate(...);
...
return $.when(elementA, elementB,...);
};
Same for b.makeStuffHappen, etc.
$.when() [docs] returns a deferred object where you can attach callbacks to which are executed once all deferred objects passed to it are resolved.
Then in your event handler, you can call .pipe() [docs] on these deferred objects, to chain them together:
$('button').click(function(){
a.makeStuffHappen()
.pipe($.proxy(b.makeStuffHappen, b))
.pipe($.proxy(c.makeStuffHappen, c))
.then(function() {
console.log('All done');
});
});
DEMO
A callback would be the easiest way, but since you don't want to use a callback (which jQuery's .queue() does require - well... a recursive callback anyways), you could let the methods you have return a bool:
$('button').click(function()
{
var theObjects = [a,b,c,d];
for (var i=0;i<theObjects.length;i++)
{
if (theObjects[i].makeStuffHappen() === false)
{
break;//or throw, or whatever...
}
}
});
Of course, it is well possible the methods may vary depending on which object is called. You could work around this by using an object instead of an array, in which case your event handler could look something like:
$('button').click(function()
{
var theObjects = {a:'aMethod',b:'bMethod',c:'cMethod'};
for(var i in theObjects)
{
if (theObjects.hasOwnProperty(i))
{
if(window[i][theObjects[i]]() === false)
{
break;//or throw, or return...
}
}
}
});
Mind you, I don't know why you don't want to use some form of callback here, as it would be the easiest way to go... but that is, of course, none of my concern... :-)
Well, had a quick look at your game... seems fun, keep working on it :). BTW, you're one step away from using callbacks all over the place:
$('#placeBet').click(function() {
placeBet();
});
Can be written as:
$('#placeBet').click(placeBet);
In the latter, placeBet is a callback function.
I think you're main concern is here:
player.printHand();
dealer.printHand(isDealerHidden);
What I'd do here, is change the printHand function, by adding this at the end:
if (this.name !== 'dealer')
{
dealer.printHand(isDealerHidden);
}
I haven't read enough of your script to make this work as you need it to, but you get the idea... An easier way would be not to animate the dealer explicitly, but alter all animators of the players hand to end with some code that animates the dealer after the players animation is completed... I know, I'm talking gibberish, but I'm at work... and sick... Good luck, anyway. I might take some more time checking your progress, maybe read your script more closely tonight and edit this dribble to something that will actually work :-)
Take a look at jquery's queue function:
http://api.jquery.com/queue
You could add a callback function to your method :
this.makeStuffHappen = function(callback) {
//do things like animations
$(this).animate({ ... }, 200, callback);
//or call the callback after everything is done:
callback();
}
and you can use it like this:
$('button').click(function(){
a.makeStuffHappen(b.makeStuffHappen(c.makeStuffHappen(d.makeStuffHappen())));
});

jQuery: How can I perform an action after .css has executed?

So my issue is pretty straight forward, since there is seemingly no callback for after a .css is executed, what options do I have for making performing something after a task is done?
I'm creating a simple lightbox, and I need to wait for the center align to finish...
$("#img_lightbox").css("top", top);
So when that completes, I then need to fade in the whole thing, but since there is no callback option (to the best of my knowledge) it will occasionally start fading in before the alignment finishes... how can this prevented?
Any help is appreciated, thank you.
Anything being chained with your jQuery object will execute after the function before it. The easiest way to accomplish what you are asking is with Plugins.
jQuery.fn.myPlugin = function () {
//code to execute
return this;
}
$("#img_lightbox").css("top", top).myPlugin();
You could even write a plugin to execute a custom function, so you do not need to create plugins for every function you might happen to need to run:
jQuery.fn.myCallback= function (callback) {
this.each(function () {
callback.call($(this));
});
return this;
}
$("#img_lightbox").css("top", top).myCallback(function () {
// some code to run where this = $("#img_lightbox")
});
But incase I am still misunderstanding, you may be wanting a callback for your fade function: (otherwise please clarify more)
$("#img_lightbox").fadeIn('slow', function () {
$(this).css("top", top)
});
Adding and removing CSS styles are synchronous functions -- there is no callback because the next statement will be executed once the style has been applied. Rendering of the updated style is slightly different, since that will happen during the next repaint, but unless you're doing some serious number-crunching in your UI code, the difference would be completely unnoticeable. In any case, it would be applied before the 'fading in' starts to happen.
If you're seeing something wrong with your display, I'd suggest that the problem lies elsewhere.
jQuery provides you with a way to tell once the document is "ready" and in the correct state to execute code correctly. Use one of the following:
$(document).ready(function(){
//your code here
});
or the shorter,
$(function(){
//your code here
});
More information at http://api.jquery.com/ready/

Categories

Resources