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.
Related
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);
});
I'm kind of new to javascript and seriously, this async thing is driving me crazy.
I'm working on a project for displaying a div (which occupies all the screen) that reloads everytime with a different content and stays on screen for an X amount of time.
For example, I already created some static screens objects inside an array like this:
screenArray[0] = {ID:"987234", Name:"SampleScreen", Time:6000};
screenArray[1] = {ID:"837625", Name:"SampleScreen2", Time:10000};
So this is pretty much what I wanted to do if javascript worked synchronously:
function ScreenEngine(){
reloadScreenContent();// "loads" the first screen (this is just an Ajax div reload function)
for (var i = 0; i == screenArray.length+1; i++){
if (i == screenArray.length){ //when it gets to the latest screen, it goes to the first one
i = 0;
}
setTimeout(reloadScreenContent, screenArray[i].Time); // loads the second screen with the timeout of the first one (i=0)
}
}
(I'm just working on the timey wimey thingy, I'll deal with the content later.)
Saw some other posts about callback functions so javascript would work "synchronously" for the things I want and I even tried some of it and failed miserably doing it.
Even not understanding with details how I would make a callback function, I understand that (at least the way I "tried") it would stack forever because I'm asking javascript to do an infinite job.
I need a brainstorm how to solve this problem, maybe some tips using callback or a similar sample code so I can guide myself.
I think what you need to do is call the reload function inside the callback function itself.
If you're using jQuery for the ajax function, the code could look something like this:
function loadContent(){
$.getJSON("yourURL", callback)
}
function callback(data){
/*do something with the data*/
/*call loadContent at an interval */
window.setTimeout(loadContent, 2000 );
}
loadContent();
This was loadContent will get called 2s after the content has been loaded. Here's an example of a recursive ajax call, you can see in the console it's making the ajax call every 10s http://jsfiddle.net/w66peL7b/1/
I have a function that is bound to mouse click events on a Google Map. Due to the nature of the function it can take a few moments for processing to complete (.1sec - 2sec depending on connection speeds). In itself this is not much of a problem, however if the user gets click happy, this can cause problems and later calls are a bit depended on the previous one.
What would be the best way to have the later calls wait for previous ones to complete? Or even the best way to handle failures of previous calls?
I have looked at doing the following:
Using a custom .addEventListener (Link)
Using a while loop that waits previous one has processed
Using a simple if statement that checks if previous one needs to be re-run
Using other forms of callbacks
Now for some sample code for context:
this.createPath = function(){
//if previous path segment has no length
if (pathSegment[this.index-1].getPath().length === 0){
//we need the previous path segment recreated using this same function
pathSegment[this.index-1].createPath();
//now we can retry this path segment again
this.createPath();
}
//all is well, create this path segment using Google Maps direction service
else {
child.createPathLine(pathSegment[this.index-1].getEndPoint(), this.clickCoords);
}
}
Naturally this code as it is would loop like crazy and create many requests.
This is a good use case for promises.
They work like this (example using jQuery promises, but there are other APIs for promises if you don't want to use jQuery):
function doCallToGoogle() {
var defer = $.Deferred();
callToGoogleServicesThatTakesLong({callback: function(data) {
defer.resolve(data);
}});
return defer.promise();
}
/* ... */
var responsePromise = doCallToGoogle();
/* later in your code, when you need to wait for the result */
responsePromise.done(function (data) {
/* do something with the response */
});
The good thing is that you can chain promises:
var newPathPromise = previousPathPromise.then(
function (previousPath) { /* build new path */ });
Take a look to:
http://documentup.com/kriskowal/q/
http://api.jquery.com/promise/
To summarize promises are an object abstraction over the use of callbacks, that are very useful for control flow (chaining, waiting for all the callbacks, avoid lots of callback nesting).
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 have been trying to chain this last bit of code to work nicely.
It calls two functions that have been preset:
slideToImage(newIndex);
setCaption();
I want the caption to be set once the image has slid.
This has always been a confusing topic for me as I have tried different ways of calling it such as:
callbacks = $.Callbacks();
callbacks.add(slideToImage(newIndex));
callbacks.add(setCaption());
callbacks.fire();
But that does not work.
slideToImage must contain some animation logic that happens over time. So slideToImage would start the animation process and then return before the animation has completed which causes setCaption to be called to early.
You should pass setCaption into slideToImage as a callback and call it once the animation has completed.
function slideToImage(newIndex, callback) {
// I assume you're using jQuery for animation
targetElement.animate({left: 100}, 200, function() {
// once animation has finished we call the callback function
callback();
});
}
slideToImage(newIndex, function() {
setCaption();
});
For "setCaption", it's easy:
callbacks.add(setCaption);
For "slideToImage", since you need to pass a parameter, it's slightly more involved:
callbacks.add(function() { slideToImage(newIndex); });