fade / css class transition happening out of order - javascript

I have several page elements I want to fade out. I then change the css class of them (while they are not visible) then fade them back in.
I thought I had ordered the execution flow properly but sure enough the css class transition is occurring before the fadeOut is complete. Visually what happens is that a person sees the css change and then fadeout occurs.
You can see it at the link below. Between slide 1 & 2 it happens but is not as noticeable as the css change is from class a to class a. Between slide 2 & 3 you can see it as that is from class a to class b.
http://staging.alexandredairy.com
jquery transition code onReady kicks it off:
var txtread =
{
onReady: function(_imgname)
{
txtread.fadeoutText(_imgname);
txtread.fadeinText();
},
fadeoutText: function(_imgname)
{
$("#pagetitle").fadeOut(1250);
$("#pagemenu").fadeOut(1250);
$("#pageslogan").fadeOut(1250);
$("#sitecopy").fadeOut(1550, txtread.TextReadabilityHandler(_imgname));
},
fadeinText: function()
{
$("#pagetitle").fadeIn(1250);
$("#pagemenu").fadeIn(1250);
$("#pageslogan").fadeIn(1250);
$("#sitecopy").fadeIn(1250);
},
TextReadabilityHandler: function(_imgNameSwitch)
{
if(_imgNameSwitch == 'Light')
{
$("#pagetitle").attr('class', 'sitetitle lighttextbackground');
$("#pagemenu").attr('class', 'sf-menu lighttextbackground');
$("#pageslogan").attr('class', 'slogan lighttextbackground');
}
else if (_imgNameSwitch == 'Dark_')
{
$("#pagetitle").attr('class', 'sitetitle darktextbackground');
$("#pagemenu").attr('class', 'sf-menu darktextbackground');
$("#pageslogan").attr('class', 'slogan darktextbackground');
}
else
{ alert(_imgNameSwitch); }
}
}
so I thought order of execution, longer fadeOut, and setting the fadeOut completed function last would keep things in order but alas. I was wrong.
Thank You
Edit
So now I have tried window.setTimeout and it behaves exactly the same as if the timeout doesn't even run???
OK my bad. I originally tried:
window.setTimeout(txtread.TextReadabilityHandler(_imgname), 3000);
and that didn't error or work. I then went back and reread a bit better and saw to use a callback so I rewrote this way:
window.setTimeout(function(){ txtread.TextReadabilityHandler(_imgname); }, 3000);
and now it is working.
My original question still applies though. I understand javascript is an asynchronous programming language but it is imperative no?? Perhaps I am getting terms jumbled in my head.
Does the following execute one after the other:
alert('1');
alert('2');
alert('3');
or do they all execute at once?

Your code includes the following.
onReady: function(_imgname)
{
txtread.fadeoutText(_imgname);
txtread.fadeinText();
}
You are correct in thinking that txtread.fadeinText() will not run until txtread.fadeoutText(_imgname) is complete. However, fadetextOut is completing before you are expecting it to.
fadeoutText: function(_imgname)
{
$("#pagetitle").fadeOut(1250);
$("#pagemenu").fadeOut(1250);
$("#pageslogan").fadeOut(1250);
$("#sitecopy").fadeOut(1550, txtread.TextReadabilityHandler(_imgname));
}
will return almost immediately, having told the various elements to fade out over a period of time. So calling txtread.fadeinText() will not wait for those elements to fade out.
You will need to add some form of callback to fadeinText and fadeoutText, which you can use to let other code know they have finished, like so.
onReady: function(_imgname)
{
txtread.fadeoutText(_imgname, function () {
txtread.fadeinText();
});
}
fadeoutText: function(_imgname, cb)
{
$("#pagetitle").fadeOut(1250);
$("#pagemenu").fadeOut(1250);
$("#pageslogan").fadeOut(1250);
$("#sitecopy").fadeOut(1550, function() {
txtread.TextReadabilityHandler(_imgname);
cb();
});
}
fadeinText: function(cb)
{
$("#pagetitle").fadeIn(1250);
$("#pagemenu").fadeIn(1250);
$("#pageslogan").fadeIn(1250);
$("#sitecopy").fadeIn(1250, cb);
}

The alerts would execute in order. Javascript is single threaded.
Edit: Er, I guess that is true for the most part
Check out this link for a good explination, especially regarding fades, etc.
Is JavaScript guaranteed to be single-threaded?

As Michael said, the javascript will all run at the same time. So, run your fadeout functions, then, once they're finished, call the csschange functions and, once that's complete, run the fade in functions.
I'd be able to write this in jQuery (as it's easy to add a function to run after another is complete). It should be straighforward in plain js, I just don't know the syntax so well . . .

in javascript commands are excuted in order , the thing is if you have error within just one command , it could stop the whole script from excution .
http://www.w3schools.com/js/js_statements.asp
Each statement is executed by the browser in the sequence they are
written.

Related

Lock simultaneous JavaScript invocation

I have a simple JavaScript function that manipulates the DOM (heights of elements for layout reasons).
The function get's called on window's resize (throttled to 1s) and on button click.
In my function everything is wrapped inside a _.delay() function in order for the script to wait 1s for a triggered animation to finish.
The problem is that sometimes the function get's called fast on after another and the second call starts before the first call ending. Now the function calls are doing their things simultaneously and everything get's bad.
My question:
How can I tell the function to only run one at a time? Some kind of lock would be good that locks the second call from executing. It would be great if this second call still executes, but only after the first call remove the lock.
Is something like this possible in JavaScript and how?
EDIT
Here is a code example of how the script looks like:
function doStuff() {
var stuff = $('[data-do-stuff]');
var height = stuff.height();
// Add CSS class that changes height of stuff
// Class starts an animation of duration of 1s
stuff.addClass('active-stuff');
// Wait 1s for the animation started by added class
_.delay(function() {
stuff.height(height * 42);
}, 1000);
}
$(window).on('resize', _.throttle(function() {
doStuff();
}, 1000));
$('.tasty-button').on('click', function() {
doStuff();
});
This is not a working example, just a gist of what the general structure of my script is.
If I e.g. click multiple times on the tasty button (about 3x in 1s) it messes with everything. (In my real script, I have got more trigger so just disabling the button for 1 second doesn't do the trick -.-)
I would like it to behave like this: If doStuff executes, lock every call until doStuff finishes with executing and then execute the locked calls afterwards.
PROMISES in Javascript is what you are looking for.
Without code examples, it's hard to suggest solutions specific to your question. However, here's some thoughts on your overall problem:
What you're experiencing is a called a "race condition" where a part of your application depends on multiple functions finishing at undetermined times.
Generally, there are two ways to handle situations like this:
1) Use callbacks. About Callbacks
2) As another user suggested, use JS promises. About JS Promises

How to display spin.js before executing some function?

I want to display a spinner before some complicated function, i.e. dummyCounter(). The code looks like:
function add1() {
msg.html('start counting~<br \>');
document.body.appendChild(div);
spinner.spin(div);
// display spinner before doing stuff
dummyCounter();
}
jsfiddle: http://jsfiddle.net/eGB5t/
However the spinner shows after the dummyCounter() function is finished.
I try to use callback to force spinner display earlier but still no good. Can anybody help? Thanks.
jsfiddle: http://jsfiddle.net/eGB5t/2/
You have a thinking failure. Spinners are usually used for asynchronous tasks, so you can see that there is something in progress. A callback is then used to remove the spin when the async action has finished, since you cannot tell before it starts when it will finish.
I made up a quick example to show you, how such an async function would work in this case, and you can clearly see how the spinner appears slightly before "google finished" appears.
http://jsfiddle.net/eGB5t/4/
I added the following instead of your counting method:
$.ajax("http://google.de").always(function() {
msg.append("google finished");
});
You add the spin before you count, then it counts, then you could remove the spinner. This is perfecty fine. Thing is, if you would count to let's say 9999999999999 (so it would take some seconds), a normal for loop like you're doing is completely blocking the browser, so you won't have any repaints (and therefore no spinner) at all, while the loop is running.
What you would have to do (in this case) is to introduce a worker to have multithreading functionality in javascript.
var x;
function add1() {
msg.html('start counting~<br \>');
spinner.spin(div);
x= setTimeout(document.body.appendChild(div),500);
}

javascript setInterval not keeping it's timing properly

I've got multiple elements on my page that fade in and out on a timer using javascript setInterval to set them in motion. I have them delayed so they are offset just slightly to create a nice cascading effect, but if you leave the page open long enough, they all catch up to one another and the timing gets all messed up (you've got to leave it for a few minutes).
I have an ugly example of the issue at CodePen here: http://www.cdpn.io/wgqJj
Again, you've got to leave the page open and untouched for a few minutes to see the problem. If you had more items on the page (5 or 10) the problem becomes even more apparent. I've also used this type of effect with several jQuery photo rotator plugins, and over time, the issue always crops up.
Is there any explanation for this?
Here is the code I'm using (I know the javascript could be cleaner):
HTML:
<p id="one">First</p>
<p id="two">Second</p>
<p id="three">Third</p>
JavaScript:
$(document).ready(function() {
var timer1 = setTimeout(startOne,1000);
var timer2 = setTimeout(startTwo,2000);
var timer3 = setTimeout(startThree,3000);
});
function startOne () {
setInterval(flashOne,3000);
}
function startTwo () {
setInterval(flashTwo,3000);
}
function startThree () {
setInterval(flashThree,3000);
}
function flashOne () {
$("#one").fadeTo("slow",0.4).fadeTo("slow",1.0);
}
function flashTwo () {
$("#two").fadeTo("slow",0.4).fadeTo("slow",1.0);
}
function flashThree () {
$("#three").fadeTo("slow",0.4).fadeTo("slow",1.0);
}
Question has already been answered here. Quoting from the top rated answer in this topic:
it will wait AT LEAST 1000MS before it executes, it will NOT wait exactly 1000MS.
Giving an actual answer, I'd solve it like this:
$(function(){
setTimeout(flashOne,1000);
});
function flashOne () {
$("#one").fadeTo("slow",0.4).fadeTo("slow",1.0);
setTimeout(flashTwo,1000);
}
function flashTwo () {
$("#two").fadeTo("slow",0.4).fadeTo("slow",1.0);
setTimeout(flashThree,1000);
}
function flashThree () {
$("#three").fadeTo("slow",0.4).fadeTo("slow",1.0);
setTimeout(flashOne,1000);
}
Like this it's not possible for the timers to mess up, as it's always delayed one second after the item before has flashed.
Consider using a chained setInterval instead as this give a guaranteed slot to the browser. Reference this SO post..
Currently you only use setInterval to start the animation. From there jQuery is handling the "oscillations".
Theoretically using a chained set interval should guarantee a slot, to the browser. More importantly, you can hard code the offset into the code at each interval, instead of only once at the beginning.
The setTimeout() and setInterval() functions do not guarantee that your events run exactly on schedule. CPU load, other browser tasks, and similar can and will affect your timers, therefore they are not reliable enough for your use case.
A solution for this would be asynchronous events (promises or similar) or using the event queue that jQuery supplies. That way you could either nest with callbacks, or queue them up and then fire the queue over again once it hits the last item in the queue. The .queue() API documentation page has an example of this.

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/

Show a message box which slides out, delays for 3 seconds and slides in with Mootools?

I'm creating a error message displaying box which slides out, delays for 3 seconds and then slides in with Mootools. This is what I'm currently doing now, how can I correct it to get it work for me?
var slide = new Fx.Slide($("error"));
slide.slideOut('horizontal').chain(function(){
$("error").set("text", message);
}).chain(function(){
this.slideIn('horizontal').delay(3000);
}).chain(function(){
this.slideOut('horizontal');
});
You basically have your mootools correct, but are missing a few key items that would make your script function properly. I have pasted a working version below, and then made some comments:
var slide = new Fx.Slide($("error"));
slide.slideOut('horizontal').chain(function () {
$('error').set('text', message); this.callChain(); //NOTE
}).chain(function () {
this.slideIn('horizontal');
}).chain(function () {
this.slideOut.delay(3000, this, 'horizontal'); //NOTE
});
Notice the this.callChain() on the
3rd line. Not having this was what
was stopping you seeing anything.
The Fx class uses the callChain()
method internally to start the next
step in the sequence, but if your
argument to chain() doesn't contain
one of Fx's methods, callChain() is
not called, so you have to do it
manually.
Your call to delay was in the wrong place. Delay() delays the execution of the function it is applied to, it does not insert a pause into a chain. Therefore to display the error message for 3sec you need to add delay to the the last function call, because this is the one you want to slow down
Your call to delay was incorrect. Delay applies to the function, not the return value of the function, hence Dimitar's suggestion above. Have a look at function in the mootools core documentation for more info
By the sounds of it, you do not have firebug installed. This would have let you explore the DOM to find that your code changes the margins and then the text, but nothing happens after that. Firebug is super useful, so install it ASAP
My solution (mootools 1.3) is below, and basically relfects what dimitar was suggesting:
$('error').set('slide', {
mode: 'horizontal'
}).get('slide').slideOut().chain(function () {
$('error').set('text', message); this.slideIn();
}, function () {
this.slideOut.delay(3000, this);
});
Hope it helps

Categories

Resources