Make a Testimonial Scroller Stay on the Screen Before Fading Out - javascript

I have a testimonial scroller that shows one testimonial, fades out, shows the next, fades out, and returns to the first.
My issue is that after the fade in animation, the fade out animation begins immediately. It doesn't give enough time for someone to read it.
$(document).ready(function() {
function doFade() {
$("#one").fadeIn(6000,function() {
$("#one").fadeOut(6000).delay(3000);
setTimeout(fadeTwo,6000);
});
}
function fadeTwo() {
$("#two").fadeIn(6000,function() {
$("#two").fadeOut(6000).delay(3000);
setTimeout(fadeThree,6000);
});
}
function fadeThree() {
$("#three").fadeIn(4000,function() {
$("#three").fadeOut(6000).delay(3000);
setTimeout(doFade,6000);
});
}
doFade();
});

jQuery's delay function will only delay functions that are called after it in the chain, so it is having no effect on your code. Delay docs
You need to use it before the call to fadeOut, e.g.
$(document).ready(function() {
function doFade() {
$("#one").fadeIn(6000,function() {
setTimeout(fadeTwo,6000);
})
.delay(3000)
.fadeOut(6000);
}
function fadeTwo() {
$("#two").fadeIn(6000,function() {
setTimeout(fadeThree,6000);
})
.delay(3000)
.fadeOut(6000);
}
function fadeThree() {
$("#three").fadeIn(6000,function() {
setTimeout(doFade,6000);
})
.delay(3000)
.fadeOut(6000);
}
doFade();
});
Edit:
You are currently setting a timeout to execute the next function, within the complete callback of fadeIn. This is a bit confusing to my mind, and I think it is simpler and clearer to do something like the following. In addition, there is no reason to define the three functions within the ready function - it is personal preference but I like to keep the amount of code within a callback to a minimum, such as...
$(document).ready(function() {
doFade();
});
function doFade() {
setTimeout(fadeTwo,12000);
$("#one").fadeIn(6000).delay(3000).fadeOut(6000);
}
function fadeTwo() {
setTimeout(fadeThree,12000);
$("#two").fadeIn(6000).delay(3000).fadeOut(6000);
}
function fadeThree() {
setTimeout(doFade,12000);
$("#three").fadeIn(6000).delay(3000).fadeOut(6000);
}
Edit 2:
In further effort to reduce the amount we repeat ourselves, we can extract the whole animation sequence into a function:
$(document).ready(function() {
doFade();
});
function fadeInThenOut(element) {
element.fadeIn(6000).delay(3000).fadeOut(6000);
}
function doFade() {
setTimeout(fadeTwo,12000);
fadeInThenOut($("#one"));
}
function fadeTwo() {
setTimeout(fadeThree,12000);
fadeInThenOut($("#two"));
}
function fadeThree() {
setTimeout(doFade,12000);
fadeInThenOut($("#three"));
}
Edit 3:
At this point we probably notice how similar our three functions are, and want some way to reduce that repetitiveness. So we could use recursion, and just change which element we pass in each time.
$(document).ready(function() {
doFade();
});
function doFade(elementNumber) {
const elementNumber = elementNumber < testimonialElements.length ? elementNumber : 0;
setTimeout(doFade(elementNumber + 1),12000);
$('#' + testimonialElements[elementNumber]).fadeIn(6000).delay(3000).fadeOut(6000);
}
var testimonialElements = ["one","two","three"];
While this solution may lose something in readability and simplicity, the great advantage is that when you add a fourth testimonial, you don't need to write a function to handle it. All you would do is change the testimonialElements array to include the new element id.

Related

Defer a Function Until Several Animations Complete

I've found a lot of questions about deferring, promises, running javascript synchronously, etc. and I've tried numerous things already but still can't get this to work.
Edit Here's a little more explanation on the problem. fetchData has a routine that depends on all the code inside showStuff being complete. In particular, there's divs that get created using percentage of screen size, and we need to get the height of those divs so we can draw gauges inside them. fetchData is running before slideDown() is complete. Please see the additional console.log code I've added directly below.
My button onClick() calls showOverlay().
function showOverlay() {
showStuff().promise().done( function() {
console.log($("#gauge1").height()); //returns -0.5625 or something close
fetchData(); //ajax call
});
}
function showStuff() {
$("#overlay").fadeIn(200);
$("#gauges").slideDown(800);
$(".gauge").each(function() {
$( this ).show(); //unhides #gauge1 div
});
}
The error I'm getting says: cannot call method 'promise' of undefined.
I'm not showing my fetchData() function but it basically uses ajax to call a web service and then creates gauges on the screen using Raphael. If fetchData runs before the animations are complete the gauges are not displayed correctly because their size is relative to the .gauge div's.
Edit1
Neither of the examples below work. They both run without errors but return too quickly.
function showOverlay() {
showStuff().promise().done(function() {
fetchData();
});
}
function showStuff() {
var def = $.Deferred();
$("#overlay").fadeIn(200);
$("#gauges").slideDown(800);
$(".gauge").each(function() {
$( this ).show();
});
def.resolve();
return def;
}
Doesn't work either:
function showOverlay() {
$.when(showStuff()).done(function() {
fetchData();
});
}
function showStuff() {
$("#overlay").fadeIn(200);
$("#gauges").slideDown(800);
$(".gauge").each(function() {
$( this ).show();
});
}
You've 2 issues, the deferred and thats not how you run animations one after the other.
This will get you part of the way:
function showStuff() {
var deferred = $.Deferred();
$("#overlay").fadeIn(300,function(){
$("#gauges").slideDown(800,function(){
$(".gauge").show(); //doing this one after another takes more code.
deferred.resolve();
});
});
return deferred;
}
Heres the codepen: http://codepen.io/krismeister/pen/pvgKj
If you need to do sophisticated animations like this. You might find better results with GSAP.
Heres how to stagger:
http://www.greensock.com/jump-start-js/#stagger
Try to use $.when() instead:
$.when(showStuff()).done(function() {
fetchData();
});
You a) need to return something from showStuff b) should return a promise directly, so that the .promise() method is unnecessary:
function showOverlay() {
showStuff().done(function() {
fetchData();
});
}
function showStuff() {
return $("#overlay").fadeIn(200).promise().then(function() {
return $("#gauges").slideDown(800).promise();
}).then(function() {
return $(".gauge").show().promise();
});
}

I have a nested function in a function but my code won't clear the display correctly. I am not getting an error in console?

$(document).ready(function()
{
function clearTheDisplayInitial()
{
$(document.getElementById('Resume')).hide();
$(document.getElementById('CodingExamples')).hide();
$(document.getElementById('AboutMe')).hide();
}
function clearTheDisplay()
{
$(document.getElementById('Resume')).fadeOut(900);
$(document.getElementById('CodingExamples')).fadeOut(900);
$(document.getElementById('AboutMe')).fadeOut(900);
$(document.getElementById('mainMenu')).fadeOut(900);
}
$("#displayResume").click(function()
{
clearTheDisplayInitial();
$(document.getElementById('Resume')).fadeIn(900);
});
$("#CodingExamples1").click(function()
{
clearTheDisplay();
$(document.getElementById('AboutMe')).fadeIn(900);
});
});
I can't get the screen to clear with the two methods. Console doesn't recognize that the functions exist. I do get the fade in features to work though
I recommend just declaring the functions outside of the $(document).ready(function(){ .. });
The console doesn't recognize that the functions exist because you are basically trying to make local functions.
function clearTheDisplayInitial() {
$("#Resume, #CodingExamples, #AboutMe").hide();
}
function clearTheDisplay() {
$("#Resume, #CodingExamples, #AboutMe, #mainMenu").fadeOut(900);
}
$(document).ready(function() {
$("#displayResume").click(function() {
clearTheDisplayInitial(); // you could just add $("#Resume, #CodingExamples, #AboutMe").hide(); here
$("#Resume").fadeIn(900);
});
$("#CodingExamples1").click(function() {
clearTheDisplay(); // you could just add $("#Resume, #CodingExamples, #AboutMe, #mainMenu").fadeOut(900); here
$("#AboutMe").fadeIn(900);
});
});
I simplified your code by the way. I hope you didn't really need the functions to be inside of the document ready function.

clearTimeout() outside of function not working properly

I have this line of code, but it doesn't work, and I'm guessing that my function is the cause of the problem here. Here's my JavaScript:
$(document).ready(function () {
var interval;
function move(ele) {
$(ele).animate({
'background-position-y': '0px'
}, 200, function () {
$(ele).animate({
'background-position-y': '3px'
}, 200, function () {
interval = setTimeout(function () {
move(ele)
}, 3);
});
});
};
$(".up").hover(function () {
move(this), function () {
clearTimeout(interval);
interval = null;
$(this).css("background-position", "80px 3px ");
};
});
Can someone explain me what I'm doing wrong here?
Even with the proper closing braces as suggested by David there is still a problem which keeps the animation going. Clearing the timer (interval) doesn't stop the callback functions passed to .animate() from executing. So interval = setTimeout(...) will still get executed and perpetuated the animation cycle.
I reworked the code a bit for a working example, though there could be some improvements (like getting rid of a global variable). http://jsfiddle.net/aKKRk/
err, it looks like your actual problem is that you're only passing one function to hover, not two. You've got ….hover(function() { move(…), function() { … } }) instead of ….hover(function() { move(…); }, function() { … }).
In the future, this kind of error will be much easier to spot if you make a habit of consistently indenting your code.

Animate forever loop works in Chrome but not in IE

I have the following snippet of code that loops through the jQuery animate function endlessly. It works fine on Chrome, but fails after the first animate call on IE. My questions are:
How can I make this work in IE (9)?
How can I add a delay after the first loop? I want there to be a delay between consecutive pulses.
#container {
position : relative;
width : 500px;
height : 200px;
overflow : hidden;
opacity: 1;
}
.
#container > img {
position : absolute;
top : 0;
left : 0;
}
.
$(window).load(function(){
$(function () {
var $image = $('#container').children('img');
function animate_img() {
if ($image.css('opacity') == '1') {
$image.animate({opacity: '0.4'}, 2000, function () {
animate_img();
});
} else {console.log('2');
$image.animate({opacity: '1'}, 2000, function () {
animate_img();
});
}
}
animate_img();
});
});
.
<div id="container">
<img src="blah.jpg" width="500" height="375" />
</div>
Remove the console.log() statement from the else branch and it should work in IE - IE doesn't like console.log() unless the console is actually open, whereas (most) other browsers either ignore it or log in a way you can see if you open the console later. (I don't have IE9, but that's all it took to fix it when I tested it in IE8.)
Also it doesn't make sense to have a document ready handler inside a $(window).load() handler, so you should remove one or the other.
As far as adding a delay between consecutive pulses, just use jQuery's .delay() function before calling .animate() in the else branch, like this:
$(function () {
var $image = $('#container').children('img');
function animate_img() {
if ($image.css('opacity') == '1') {
$image.animate({opacity: '0.4'}, 2000, function () {
animate_img();
});
} else { // console.log removed from this spot
$image.delay(500).animate({opacity: '1'}, 2000, function () {
animate_img();
});
}
}
animate_img();
});
P.S. Given that the anonymous functions you've got for the .animate() complete callbacks don't do anything except call animate_img() you can remove the anonymous functions and just pass animate_img directly. So you can make the function much shorter if you wish:
$(function () {
var $image = $('#container').children('img');
function animate_img() {
var fade = $image.css('opacity') == '1';
$image.delay(fade?1:500).animate({opacity:fade?'0.4':'1'},2000,animate_img);
}
animate_img();
});
After a deleted discussion about if setTimeout() or setInterval() should be used, a different solution using setInterval()
$(function () {
var $image = $('#container').children('img');
function animate_img() {
if ($image.css('opacity') == 1) {
$image.animate({opacity: 0.4},{ queue:false, duration:2000});
} else {
$image.animate({opacity: 1},{ queue:false, duration:2000});
}
}
setInterval(animate_img, 4000);
});
Notice that interval time should have a minimum of 4000ms, which the sum of the animation time (2000ms each) back and forth. If the interval is less than that, the animations won't be completed.
I insisted in the previous (deleted) discussion that the correct syntax for setInterval should be setInterval("animate_img()", 4000) ... my bad, because I missed that setInterval was within a function ... so the function animate_img should be called as a pointer rather than a string.
The advantage of this solution (I think) is less lines of code but also that we don't need to call the function animate_img() 3 times in the loop and from within itself.
This should work fine in Chrome as well as in IE.

Refactor $(document).ready(), currently have 2 instances

I've got following JavaScript functions but want to refactor the $(document).ready() as I've got 2 instance of it. How can I achieve this?
FlashMessenger = {
init: function() {
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
}
}
SelectLanguage = {
init: function() {
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
}
}
$(document).ready(FlashMessenger.init);
$(document).ready(SelectLanguage.init);
It’s perfectly acceptable to set multiple handlers for $(document).ready, although you may have a good reason to do otherwise that I’m not aware of. You might be interested in knowing that $(handler) can be used as shorthand for $(document).ready(handler):
$(FlashMessenger.init);
$(SelectLanguage.init);
If you really want them in one call though, try this:
$(function() {
FlashMessenger.init();
SelectLanguage.init();
});
First off, there's no reason you have to combine them.
But if you want to:
$(document).ready(function(jq){
FlashMessenger.init(jq);
SelectLanguage.init(jq);
});
Breaking it down:
Create a function to do all your init (it can be named or anonymous; the one above is anonymous).
Have it call the other init functions, passing in the jQuery instance that jQuery passes you just in case they use it.
You might choose to wrap each init call in a try/catch block as well, so that errors in one init don't prevent the next init from occuring, but that depends on your needs.
Just combine them into one call with an anonymous function:
$(document).ready(function()
{
FlashMessenger.init();
SelectLanguage.init();
});
$(document).ready(function() {
FlashMessenger.init();
SelectLanguage.init();
});
Option 1
FlashMessenger = {
init: function() {
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
}
}
SelectLanguage = {
init: function() {
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
}
}
$(function(){
FlashMessenger.init();
SelectLanguage.init();
});
Option 2
FlashMessenger = {
init: function() {
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
}
}
SelectLanguage = {
init: function() {
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
}
}
$(document).ready(function(){
FlashMessenger.init();
SelectLanguage.init();
});
Option 3
You actually don't need those 2 objects since the only hold the init methods, so here's the ultimate solution, in my opinion, unless you use those objects elsewhere.
$(function(){
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
})
I prefer 2 and 3 for this reason.
I think what the op is saying is, "If in the future I have a third function to be invoked at document.ready, then how do I do it without touching that piece of code?"
If you do not want multiple $(document).ready() calls, you could just create an array called startupHooks and add each method to it:
startupHooks[ startupHooks.length ] = myNewStartupHook;
and your startup script could look like
$(document).ready(function() {
for( var i=0; i<startupHooks.length; i++ ) {
startupHooks[i]();
}
}
I know that is not mighty useful, but if that appeals to you, you can do it this way.
Personally, I'd go with multiple $(document).ready() calls.
Personally I'd go for not using document.ready at all.
If you place the scripts at the end of your html-page(just before the tag) you can just write in any way you like.
Maybe this doesn't work for 0.01% of the scripts but it never failed to work for me.
Positive effect of this is that the initial HTML+CSS rendering goes faster.
You can also read about it on yahoo. http://developer.yahoo.com/performance/rules.html#js_bottom

Categories

Resources