Maintaining jQuery order of execution - javascript

In my jQuery code, I am having trouble with keeping the correct order of execution. I looked around and found out that using setTimeout() is an option but I am not sure where to use. The current structure of the code is like below with setTimeout():
$(document).ready(function(){
$('#search-submit').on('click', function(){
var keyword = $('#search-input').val();
$(".loading").show();
setTimeout(function() {
if(){
//some conditions and calls to post
}
$(".loading").hide();
}, 0);
}
}
The hide() should take effect after the if block is finished executing, but now it directly hides the element.

Actually, jQuery has a more refined way of making things synchronous :
$(function() {
$('#search-submit').on('click', function(){
var keyword = $('#search-input').val();
$('.loading').show().queue(function() {
if ( ... ) {
//some conditions and calls to post
}
$(this).dequeue();
}).queue(function() {
$(this).hide().dequeue();
});
});
});

$(document).ready(function(){
$('#search-submit').on('click', function(){
var keyword = $('#search-input').val();
$(".loading").show();
if(){
//some conditions and calls to post
}
setTimeout(function() {
$(".loading").hide();
}, 1500);
}
}

Related

Make a Testimonial Scroller Stay on the Screen Before Fading Out

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.

Adding a jQuery delay on autocomplete

I'm trying to create a jQuery autocomplete for an application:
$("#search-input").on('keyup', function() {
search = $(this).val();
autocomplete_div = $(".autocomplete")
$.get('/ajax/search/', {'search': search,}, function(response){
autocomplete_div.html(response)
});
});
What would I need to add to the above code to add a 400ms delay?
Use
setTimeout(function() {
// your code here
}, 400);
setTimeout is a method provided by the browser's window object.
A more complete example that cancels a timer if already set using clearTimeout would be:
var myTimer = 0;
$("#search-input").on('keydown', function() {
search = $(this).val();
// cancel any previously-set timer
if (myTimer) {
clearTimeout(myTimer);
}
myTimer = setTimeout(function() {
autocomplete_div = $(".autocomplete")
$.get('/ajax/search/', {'search': search,}, function(response){
autocomplete_div.html(response)
});
}, 400);
});
Also note use of on instead of the deprecated live.
Your code should look like this: (for jQuery 1.7+)
$(document).on('keyup', "#search-input", function () {
clearTimeout($(this).data('timeout'));
var _self = this;
$(this).data('timeout', setTimeout(function () {
$.get('/ajax/search/', {
search: _self.value
}, function (response) {
$(".autocomplete").html(response);
});
}, 400));
});
If using older jQuery version, use live() or better delegate(). BTW, you should bind it to closest static container, not document.
You can use the setTimeout() function to delay the start of the expression, in this case, your function. Beware that this does not delay processing beyond this code. It will only delay the start of this function, while continuing to process code after the function.
$("#search-input").live('keydown', setTimeout(function() {
search = $(this).val();
autocomplete_div = $(".autocomplete")
$.get('/ajax/search/', {'search': search,}, function(response){
autocomplete_div.html(response)
})
},400));
EDITED: to correct misplaced parentheses.

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();
});
}

Move element with jQuery plugin - setInterval

Why this code doesn't work?
I wrote This code but:
(function($){
$.fn.newsSlider = function() {
setTimeout(function() {
this.each( function() {
$(this).each(function(){
$(this).append($(this).children(":first-child").clone());
$(this).children(":first-child").remove();
});
});
}, 3000);
}
}(jQuery));
With setInterval:
http://jsfiddle.net/OmidJackson/46UNg/
Without setInterval:
http://jsfiddle.net/OmidJackson/6bKWU/
Your problem is that this has different meaning (the window object) inside the setTimeout function literal. Check this answer for further info about this in different contexts.
The solution is to save a reference to this so you can use it inside the setTimeout.
See this example.
You need to store the this as it currently has a value of window
var $this = this;
setTimeout(function() {
$this.each( function() {
$(this).each(function(){
$(this).append($(this).children(":first-child").clone());
$(this).children(":first-child").remove();
});
});
}, 3000);

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