Good day fellow coders,
After tinkering awhile, I still couldn't find a way to invoke visual effects in jQuery and trigger a function afterwards. The program either completes mere visual effects, such as a vertical flip, or solely executes the denoted function. Is there a way to firstly complete the graphical effects and trigger the included function lastly, in just one click?
Below the pertaining code:
$("#HTMLButton").click(function(){
$("#Div").slideUp(400); //Is only run
arbitraryFunction; //Ignored
});
$("#HTMLButton").click(function(){
arbitraryFunction; //Is only run
$("#Div").slideUp(400); //Ignored
});
Thank you in advance!
First thing to do is have a look at the jQuery docs
- https://api.jquery.com/slideUp/
.slideUp( [duration ] [, complete ] )
duration (default: 400)
Type: Number or String
A string or number determining how long the animation will run.
complete
Type: Function()
A function to call once the animation is complete, called once per matched element.
So rather than a Promise interface, they offer a more traditional callback interface
$("#HTMLButton").click(function(){
$("#Div").slideUp(400, () => {
arbitraryFunction()
});
});
An alternative approach (if you lacked a callback mechanism) would be a setTimeout() function:
$("#HTMLButton").click(function(){
$("#Div").slideUp(400);
setTimeout(() => {
arbitraryFunction();
}, 400);
});
Related
I was starting to make a quite complex animation with jQuery and I was looking for a certain way to animate elements together. I wrote the code somewhat as follows, which I could use to animate multiple elements together:
Here's the fiddle: http://jsfiddle.net/wkhrU/
$(document).ready(function(){
$('#firstElement, #secondElement').animate(
{left : '+=400px'},
500,
"linear",
function(){
alert("completed");
});
});
However, the callback function fires twice i.e. each time for the element inside. What I was looking for is to be something like animating multiple elements simaltaneously for the same time duration and after the completion, fire a call inside which I wanted to swap the id attrib of the elements. I can't do the same here because on completion callback, it swaps the ids once and on the next function callback, swaps them back again. What's the appropriate way to achieve this?
SOLUTION
You could maybe use a promise() to call the function to be executed instead of the inbuilt callback of animate. This ensures that the function in done is called only after animating both the elements. It's basically like you're asking #firstElement & #secondElement to promise JS that it'll inform when both of 'em are done so that you could attach a done handler to it.
$(document).ready(function () {
$('#firstElement, #secondElement').animate({
left: '+=400px'
},
500, "linear").promise().done(function () {
alert("completed");
});
});
DEMO
http://jsfiddle.net/hungerpain/wkhrU/1/
MORE INFO ON USED METHODS :
promise
Docs : http://api.jquery.com/promise/
What it does : Makes sure that all actions of a certain type bound to the collection, queued or not, have finished.
done
Docs : http://api.jquery.com/deferred.done/
What it does : Add handlers to be called when the promise is resolved.
I'm working on an interactive tutorial-tool for JavaScript. The core of the tool is the script of the tutorial. The script will trigger various functions that run animations, speaker-voices load new pages etc. Three sample calls(most tutorials will have 10-100s of calls, so a neat overview of the calls is highly desired:
wrap(); //wrap the page in an iframe
playsound('media/welcome') //playing a sound (duh)
highlight('[name=firstname]'); //animation that highlights an element.
playsound('media/welcome2');
loadpage(page2); //loading a new page
All calls have something in common: they have non-normal-triggers. In this simple script for example, the second call should be triggered once the iframe in the first call is loaded. The third script is triggered once the sound is complete (ie delay). The fourth function should be triggered once the animation is complete. The fifth event should be triggered on an event (for example a click).
A technical solution to this would be to call the function in the callback of the previous function, this has the potential to get pretty messy. What I like with a solution wherer the functions are called lite this is that someone with a little bit of brains, but no coding experience could hammer up a script of their own. How would you solve this? I'm pretty new to javascript so if you could be explicit i'd appreciate it.
I'd use a per-built solution. There is bound be one that fits your needs. Something simple like jTour or if that doesn't cover it something a little more complex like Scriptio. Some of the answers to this question may also be of interest to you.
Edit
If you don't want to use a preexisting solution, I'd do something like this:
var runTutorial = (function () {
// The command object holds all the different commands that can
// be used by someone for the tutorial. Each of these commands
// will recive a callback set as their `this`. This
// callback should be called by your commands when they are done
// running. The person making the tutorial won't need to know
// about the callback, the code will handle that.
var commands = {
wrap: function () {
//wrap the page in an iframe
this();
},
playsound: function (soundPath, soundLength) {
//playing a sound (duh)
setTimeout(this, soundLength);
},
highlight: function (selector) {
//animation that highlights an element.
//I'm using jQuery UI for the animation here,
// but most animation libraries should provide
// a callback for when the animation is done similarly
$(selector).effect('highlight', 'slow', this);
},
loadpage: function (pageUrl) {
//loading a new page
setTimeout(this, 500);
},
waitForClick: function () {
// when we go into the click handler `this` will no
// longer be availble to us since we will be in a
// different context, save `this` into `that` so
// we can call it later.
var that = this;
$(document).one('click', function () {
that();
});
}
},
// This function takes an array of commands
// and runs them in sequence. Each item in the
// array should be an array with the command name
// as the first item and any arguments it should be
// called with following as the rest of the items.
runTutorial = function (commandList) {
var nextCommand = function () {
if (commandList.length > 0) {
var args = commandList.shift();
// remove the command name
// from the argument list
cmd = args.shift(1);
// call the command, setting nextCommand as `this`
commands[cmd].apply(nextCommand, args);
}
}
nextCommand();
};
return runTutorial;
}());
$('#tutorialbutton').click(function() {
runTutorial([
['playsound', 'media/welcome', 1000],
['highlight', '[name=firstname]'],
['playsound', 'media/welcome2', 1500],
['waitForClick'],
['loadpage', page2],
['playsound', 'media/page2', 100]
]);
});
The runTutorial function takes a simple array containing the commands in the order they should be run, along with their parameters. No need to bother the person writing the script with callbacks, runTutorial handles that for them. This has some big advantages over a system that requires the writer to manage callbacks. You don't need an unique name for each line in the script as you do with explicit callbacks, nor endless nesting of anonymous functions. You don't need to rewire anything to change the order that the commands are played in, you just physically rearrange them in the array.
jsfiddle you can play with
Each of your commands will need to wait for its action to be done before it calls its callback (aka this). I simulate this in the fiddle using setTimeout. For instance, if you are using jQuery's .animate for highlight, it provides a complete handler that fires when the animation is done, just stick this (with out the invocation parentheses ()) there. If you are using jQuery UI, it has a built-in 'highlight' effect, so you could implement it like this:
highlight: function (selector) {
//animation that highlights an element.
$(selector).effect('highlight', 'slow', this);
},
Most other libraries that provide animations should provide a similar callback option you can use.
Controlling the callback for the sounds may be harder depending on how you are playing them. If the method you are using doesn't provide a callback or a way of polling it to see if it is done yet you might just have to add another parameter to playsound that takes the length of the sound in ms and then waits that long before proceeding:
playsound: function (soundPath, soundLength) {
//playing a sound (duh)
setTimeout(this, soundLength);
},
Callbacks are your best bet, I think. They don't have to be messy (though it's certainly possible to make them completely incomprehensible). You could create each function to accept a callback, then use a structure like this to call them in sequence in a readable way:
var loadingSequence = {
start : function() { wrap(this.playsound); },
playsound : function() { playsound('media/welcome', this.highlight); },
highlight : function() { highlight('[name=firstname]', this.playsound2); },
playsound2 : function() { playsound('media/welcome2', this.loadpage); },
loadpage : function() { loadpage(page2); }
};
loadingSequence.start();
I'm trying to construct a JavaScript function that accepts a transition function as one of its parameters. Keep in mind that this transition function is user built so it will have a variety of syntax, but the goal of this function is to transition the webpage from one style to another.
For example, the page might currently have a vertical three column layout and calling this transition function might change it into a horizontal two column layout.
What I need is some type of callback or wait/sleep function until the transition is complete (which is designated by the presence of a particular form object). I've been trying to use eval(), but have read several many posts on not using this. Below is an example of the code I'm looking for - no jquery or other framework please.
// MAKE ANY WEBPAGE TRANSITIONS
if (transition != '') {
eval(transition, callback) {
success: alert('done with the transition eval call');
}
}
The transition function has to be designed to accept a callback in the first place.
function transition(callback) {
// Do stuff
callback();
}
transition(function () { alert("end of transition"); });
There is no generic way to detect when a function which performs asynchronous actions (such as Ajax or setTimeout calls) has finished. The function itself has to provide a way.
recently I've encountered a problem with IE. I have a function
function() {
ShowProgress();
DoSomeWork();
HideProgress();
}
where ShowProgress and HideProgress just manipulate the 'display' CSS style using jQuery's css() method.
In FF everything is OK, and at the same time I change the display property to block, progress-bar appears. But not in IE. In IE the style is applied, once I leave the function. Which means it's never shown, because at the end of the function I simply hide it. (if I remove the HideProgress line, the progress-bar appears right after finishing executing the function (more precisely, immediately when the calling functions ends - and so there's nothing else going on in IE)).
Has anybody encountered this behavior? Is there a way to get IE to apply the style immediately?
I've prepared a solution but it would take me some time to implement it. My DoSomeWork() method is doing some AJAX calls, and these are right now synchronous. I assume that making them asynchronous will kind of solve the problem, but I have to redesign the code a bit, so finding a solution just for applying the style immediately would much simplier.
Thanks rezna
Synchronous requests block the entire UI, which is horrible. You are right in your assumption, but if you want to continue down this path to the inner circles of $hell, try this:
function () {
ShowProgress();
window.setTimeout(function () {
DoSomeWork();
HideProgress();
}, 0);
}
Note that I have not tested this. Try playing with the time-out value (currently 0) if you still do not see anything.
Try throwing the HideProgress method into a setTimeout. For example:
function() {
ShowProgress();
DoSomeWork();
setTimeout(HideProgress,0);
}
Even though the delay is supposedly 0 milliseconds, this will have the net effect of throwing the HideProcess method to the end of the queue and may give the browser the breathing room it needs.
[Edit] I should have mentioned that this method does have a drawback if you invoke this method very often and rapidly: a race condition. You could end up with a previous timeout executing while another DoSomeWork() is executing. This will happen if DoSomeWork take a very long time to finish. If this is a risk, you may want to implement a counter for your progress bar and only execute HideProgress if the counter that started it is the same as the counter's present value.
I'm using jQuery to change the HTML of a tag, and the new HTML can be a very long string.
$("#divToChange").html(newHTML);
I then want to select elements created in the new HTML, but if I put the code immediately following the above line it seems to create a race condition with a long string where the changes that html() is making may not necessarily be finished rendering. In that case, trying to select the new elements won't always work.
What I want to know is, is there an event fired or some other way of being notified when changes to html() have finished rendering ? I came across the jQuery watch plugin, which works alright as workaround but it's not ideal. Is there a better way ?
As a commenter already mentioned, JavaScript is single threaded, so you can't get race conditions.
What may trip you up however, is the fact that the UI will not update itself based on JavaScript, until a thread is finished. This means that the entire method must finish, including all code after you call html(...), before the browser will render the content.
If your code after calling html(...) relies on the layout of the page being recalculated before continuing, you can do something like this:
$("#divToChange").html(newHTML);
setTimeout(function() {
// Insert code to be executed AFTER
// the page renders the markup
// added using html(...) here
}, 1);
Using setTimeout(...) with a time of 1 in JavaScript defers execution until after the current JavaScript code in the calling function finishes and the browser has updated the UI. This may solve your problem, though it is difficult to tell unless you can provide a reproducible example of the error you're getting.
use .ready jQuery function
$("#divToChange").html(newHTML).ready(function () {
// run when page is rendered
});
It's 7 years latter and I just ran into a scenario exactly like the one #mikel described, where I couldn't avoid a "timer based solution". So, I'm just sharing the solution I developed, in case anyone out there is still having issues with this.
I hate having setTimeouts and setIntervals in my code. So, I created a small plugin that you can put where you think it's best. I used setInterval, but you can change it to setTimeout or another solution you have in mind. The idea is simply to create a promise and keep checking for the element. We resolve the promise once it is ready.
// jquery.ensure.js
$.ensure = function (selector) {
var promise = $.Deferred();
var interval = setInterval(function () {
if ($(selector)[0]) {
clearInterval(interval);
promise.resolve();
}
}, 1);
return promise;
};
// my-app.js
function runWhenMyElementExists () {
// run the code that depends on #my-element
}
$.ensure('#my-element')
.then(runWhenMyElementExists);