I am working on a font-test page that should switch the font of the header and paragraph tags on click. Which works fine but the experience is really jarring so I want to smooth it out with the following path:
fadeout -> do the font swap -> fade in
But the code runs the font swap first, then the animation.
I have the following code snippet, I am only including the jQuery because it's the root cause of my problem.
// Anytime a card is clicked
$('.card').click(function(){
// Call card by id and switch the fonts
var id = "#" + this.parentElement.id;
$(this).fadeOut(900,swapFont(id));
$(this).fadeIn(500);
// attach the above to some sort of transition callback
});
// Function to swap the font around so that the header is now the body font and the body font is now the header font
function swapFont(id) {
// font-swap logic in here which works fine
}
The issue is because the fadeOut() animation is (effectively) asynchronous. This means the next statement will be evaluated while the animation is in progress - hence the behaviour you're seeing. To fix this you need to use the callback pattern that the fadeX() methods provide.
Also, you're passing the result of the immediate execution of swapFont() to the callback, instead of a function reference to run when the event occurs. Try this:
$('.card').click(function(){
var id = "#" + this.parentElement.id;
$(this).fadeOut(900, function() {
swapFont(id);
$(this).fadeIn(500);
});
});
You are currently passing the result of the swapFont() call, instead you need to point to the function itself.
So this:
$(this).fadeOut(900,swapFont(id));
is the same as:
var x = swapFont(id);
$(this).fadeOut(900, x);
Easiest way is to wrap it in an anonymous function:
$(this).fadeOut(900, function() { swapFont(id) });
Edit:
The fadeIn will also execute before the fadeOut has completed. You can add a .delay or, better, call the fadeIn in the callback as per Rory's answer (so I won't repeat here).
Related
When I drop a debug point in my source file I can see the following image:
But when I remove this debug point, I only see the following:
The change in color is affected by an overlay with some opacity.
The relevant code is:
flashSuccessMessage = function(msg) {
$('#overlay').hide();
var $ch = $("#content-header");
$ch.after('<div id="flash_message" class="alert"></div>');
var $fm = $("#flash_message");
$fm.addClass("alert-success");
$fm.append(msg);
$fm.show().children().show();
$fm.fadeOut(3000);
$fm.empty();
}
And in this case msg is "Job Type Added Successfully"
I can't understand why I only see the message when I break on the execution after the point where I call $fm.append(msg); It doesn't matter which I break on after that (of the three lines), it will appear. When I don't have any break and let the page execute the script, the green alert appears but no words.
Any ideas?
I've tried various jQuery methods here - instead of .append() using .html() for example, and I've tried inserting the msg inside the with the flash_message id to begin, tried inserting the message wrapped in tags, tried re-ordering things, (but I need to clear the contents of this div at the end...)
I've used jQuery's .delay() method, etc. Is it jumping to executing .empty() despite other elements using a timer to fully execute?
Add a callback to your fadeOut so that the contents aren't emptied until $fm is completely hidden:
flashSuccessMessage = function(msg) {
$('#overlay').hide();
var $ch = $("#content-header");
$ch.after('<div id="flash_message" class="alert"></div>');
var $fm = $("#flash_message");
$fm.addClass("alert-success");
$fm.append(msg);
$fm.show().children().show();
$fm.fadeOut(3000, function(){ // empty() is not called until
$(this).empty(); // the animation is complete
});
}
Without the callback, the empty() method is being triggered immediately after fadeOut(). This causes the content to be emptied BEFORE the animation is complete.
More information on jQuery's fadeOut method can be found in the docs.
The animations in JQuery are asynchronous, so the code keeps executing while the animation happens. .fadeOut has a completion block which you can read about at http://api.jquery.com/fadeOut/.
So, instead of calling fm.empty() after your animation, you should put it inside a function and pass that function into fade out. That function will then run after the animation completes.
but I need to clear the contents of this div at the end...
Use html instead of append:
$fm.html(msg);
$fm.fadeOut(3000, function(){
$fm.empty();
});
Check doc of fadeOut:
.fadeOut( [duration ] [, complete ] )
complete
Type: Function()
A function to call once the animation is complete.
I want to replay my jquery function ChangeStats() every 5 seconds, it's currently doing sod all.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500);
setTimeout(function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
}, 500);
}
$(document).ready(function(){
setInterval(ChangeStats, 5000);
})();
Yes I have got the right class names.
No I haven't used underscores in my HTML.
I think it's something to do with my use of "find()", once the DOM has loaded and the function is set is it meant to traverse up the DOM tree instead of down?
EDIT:
Updated code, still not working.
HTML:
<span class="admin-stats-big-figures">%productCount%</span>
<span class="admin-stats-big-figures-hidden">hey</span>
Ok, I am going to go out on a limb and make several assumptions here; one is that you wish to cycle between two elements repeatedly, another is that you are using $(this) in the context of the window rather than a containing element. If either of these are incorrect then the following solution may not be suitable. However, let's give this a shot, eh?
1) You need to use setInterval rather than setTimeout to create a repeating call. You can of course "chain" your timeouts (ie: call the succeeding timeout from the code of the current timeout). This has some benefits in certain situations, but for now let's just assume you will use intervals rather than timeouts.
2) You call the find() jQuery method every time, which is a little unnecessary, especially if you will be repeating the actions so one idea would be to cache the lookup. If you are going to do that a custom object would be more suitable than separate global variables.
3) Some flexibility in terms of starting and stopping the animation could be provided. If we use a custom object as mentioned in (2) then that can easily be added.
4) You are using fadeIn and fadeOut, however if you wish the items to cycle then fadeToggle may be your best solution as it will simply allow you to do exactly that, toggle, without needing to check the current opacity state of the element.
5) Finally in my example I have provided a little extra "padding HTML" in order for the example to look good when run. Fading in jQuery will actually set the faded item to a CSS display of "none" which results in the content "jumping about" in this demo, so I have used some div's and a couple of HTML entity spaces to keep the formatting.
Ok, after all that here is the code..
// your custom animation object
var myAnim = {
// these will be cached variables used in the animation
elements : null,
interval : null,
// default values for fading and anim delays are set to allow them to be optional
delay : { fade: 500, anim: 200 },
// call the init() function in order to set the variables and trigger the animation
init : function(classNameOne, classNameTwo, fadeDelay, animDelay) {
this.elements = [$("."+classNameOne),$("."+classNameTwo)];
// if no fade and animation delays are provided (or if they are 0) the default ones are used
if (animDelay) this.delay.anim = animDelay;
if (fadeDelay) this.delay.fade= fadeDelay;
this.elements[0].fadeOut(function(){myAnim.start()});
},
// this is where the actual toggling happens, it uses the fadeToggle callback function to fade in/out one element once the previous fade has completed
update : function() {
this.elements[0].fadeToggle(this.delay.anim,function(el,delay){el.fadeToggle(delay)}(this.elements[1],this.delay.anim));
},
// the start() method allows you to (re)start the animation
start : function() {
if (this.interval) return; // do nothing if the animation is currently running
this.interval = setInterval(function(){myAnim.update()},this.delay.fade);
},
// and as you would expect the stop() stops it.
stop : function () {
if (!this.interval) return; // do nothing if the animation had already stopped
clearInterval(this.interval);
this.interval = null;
}
}
// this is the jQuery hook in order to run the animation the moment the document is ready
$(document).ready(
function(){
// the first two parameters are the two classnames of the elements
// the last two parameters are the delay between the animation repeating and the time taken for each animation (fade) to happen. The first one should always be bigger
myAnim.init("admin-stats-big-figures","admin-stats-big-figures-hidden",500,200);
}
);
OK, so now we need the HTML to compliment this (as I say I have added a little formatting):
<div><span class="admin-stats-big-figures">One</span> </div>
<div><span class="admin-stats-big-figures-hidden">Two</span> </div>
<hr/>
<input type="button" value="Start" onclick="myAnim.start()"/> | <input type="button" value="Stop" onclick="myAnim.stop()"/>
I have also provided buttons to stop/start the animation. You can see a working example at this JSFiddle - although the stop/start buttons are not working (presumably something specific to JSFiddle) they do work when in context though.
Here im gonna just replace your $(this). and maybe it'll work then.. also using callback.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500, function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
});
}
$(document).ready(function(){
setTimeout('ChangeStats()', 5000);
});
i want to expand a division with data that should be loaded from another server.
the problem is if i start expanding the division (toggleslide) and the load method finishs the height is jumping and the effect is destroyed.
what i want is that jquery starts expanding AFTER the data-transfer finished but my following code does not work:
// divison name = details
details.load("index.php", expandLastResultDetails3(details));
function expandLastResultDetails3(details) {
$(details).slideToggle('slow', function () {
ready();
});
}
the box is jumping because (i guess) the slidetoggle starts to early.
.load() expects a function callback to be called upon the completion of the request. However, instead of passing a callback, you are executing the function. Try the following:
details.load("index.php", function () {
expandLastResultDetails3(details);
});
I have probably a simple problem to solve but I don't know what's the right approach on that.
I have a div.notification bar on top of my website that is hidden by default. The bar does get an additional class of either success or warning that sets it to display:block; if needed.
So, there are two cases. Either an output message is rendered directly to the .notification bar on page-load (and it gets a class of success or warning right with it) OR the .notifcation bar is sliding down from top and is fed with json data.
Any way, the notification bar should always be visible for 10 seconds and than slide back up.
Therefore I wrote two functions that handle this bar.
var timing = 10000;
function notificationOutput(type, message) {
var note = $('.notification');
note.hide();
note.find('.message').html(message);
note.stop(true,true).slideDown().delay(timing).slideUp();
}
function notificationSlideUp(slideUp) {
var note = $('.notification');
if ( slideUp ) note.delay(timing).slideUp();
else note.stop(true,true).slideUp();
}
So the notificationOutput() function is triggered from various other functions that return json data and render that into the box.
And the notificationSlideUp() function is right now called on every page load because in my header I have this …
<script type="text/javascript">
$(document).ready(function(){
notificationSlideUp(true);
});
</script>
And there is a reason for that! Remember the case where the .notification is directly set to visible on page-load … e.g. when a user logs-in on my platform the .notification bar is immediately visibile and says "Welcome Username, you've successfully logged in".
I call the notifictaionSlideUp() function in my header to make sure the currently visible .notification bar will slideUp() again after 10 seconds.
And here occurs the problem …
Somehow this causes the entire notifcation timing and sliding up and down to be "confused". So there must happen some queuing of the slide-functions and or of the delay() function, because without the note.stop(true,true) in the notificationOutput() function the notification wouldn't slideDown() immediately if it's triggered within the first 10 seconds after the page load. That is because the notificationSlideUp() function has already triggered the slideUp() and the delay() of the object.
And the same happens to the delay. If a .notification is put out within the first 10 seconds the delay timing isn't right because the delay counter already started on page-load.
Any idea how to solve that so that it always works?
Update:
var notificationTimer;
function notificationOutput(type, message) {
var note = $('.notification');
note.hide();
note.find('.message').html(message);
note.stop(true,true).slideDown();
clearTimeout(notificationTimer);
notificationTimer = setTimeout(function() {
note.stop(true,true).slideUp();
}, 10000);
}
function notificationSlideUp(slideUp) {
var note = $('.notification');
if ( slideUp ) {
clearTimeout(notificationTimer);
notificationTimer = setTimeout(function() {
note.stop(true,true).slideUp();
}, 10000);
} else {
note.stop(true,true).slideUp();
}
}
Unfortunately, jQuery's delay() function doesn't offer any method for canceling a delay the way setTimeout() does with clearTimeout(). I'd suggest replacing your delay()s with named setTimeout()s, and writing a condition to clearTimeout() for cases in which you need to cancel/trigger a queued animation right away.
http://api.jquery.com/delay/
The .delay() method is best for delaying between queued jQuery
effects. Because it is limited—it doesn't, for example, offer a way to
cancel the delay—.delay() is not a replacement for JavaScript's native
setTimeout function, which may be more appropriate for certain use
cases.
But you can use the following 'hint' - replace delay with some animation which does not change anything. For example with .animate({opacity:1},timing)
I'm required to develop a slideshow (not an existing one) with jQuery. I was able to change picture with a function that I created named changePic (takes an image link). It incorporates the fading animation from the jQuery library.
For the slideshow I'm trying to use a while loop. It kind of works, except that it doesn't wait for the animation to finish.
How do I, a) wait for the animation to finish, b) delay the changing picture so it display the picture for a couple of seconds?
Also tried Settimeout, and it doesn't work.
Edit:
Basically changing image is like this:
function changePic(imglink){
var imgnode = document.getElementById("galleryimg");
$(imgnode).fadeTo(500, 0, function(){
$(imgnode).attr("src", imglink);
$(imgnode).fadeTo(1000, 1);
})
}
and the slideshow code is like this, but obviously it shouldn't.
function slideshow(gallerylinks){
var i=0;
while (i<gallerylinks.length){
changePic(gallerylinks[i]);
i++;
}
}
You could always try ditching the while loop, and going with a perpetually recursive function...
on the .animate, you could add a timeout function (at whatever interval) that calls the changePic function. As I have no idea what your code looks like, I will provide a fantastically generic outline.
/* array of imgUrls */
var imgUrls = new Array(); //populate it however
changePic(slideToShowIndex, fadeOutSpeed, fadeInSpeed, slideDelay)
{
$('#slideHolder').animate({ opacity: 0}, fadeOutSpeed , function(){
$('#slideHolder').attr('src', imgUrls[slideToShowIndex]);
$('#slideHolder').animate({ opacity: 1 }, fadeInSpeed, function() {
setTimeout(function() { changePic(slideToShowIndex+1, fadeOutSpeed, fadeInSpeed, slideDelay);}, slideDelay});
});
}});
}
$(document).ready(function() {
changePic(0, 5000, 5000, 10000);
});
This should (in theory) fade the image out, swap it with the new one, and fade it in (both taking 5 seconds) and then adding a delay to call itself with the next slide index in 10 seconds.
This is in no way perfect, but does outline the general idea. Since we have no idea what your code looks like, I can only assume your setTimeout was in the wrong spot. Doing it like this will make sure that the animation has finished before the timeout is set. This guarantees that the slide wont change until after the animation has changed.
of course you could always use a combination of the ':not(:animated)' selector and a setInterval to achieve much the same effect.
EDIT: made a slight change to stack the animations properly. The thoery behind this still works even with the OPs addition of code.
You could have provided more details or example code but have a look at stop() and delay() functions.