Calling functions from within another - javascript - javascript

I'm relatively new to javascript and having a couple of issues that I'm hoping you can help with. I'm utilizing the fabric library with canvas to create an animation of sorts.
I have a function that is called when the relevant button is pressed:
//for starting the animation
startSim.onclick = function(){
//obtain speed value from the control panel.
var speed = 10000 / (new Number(speedControl.value));
//stuff
//animation process
object.animate('top', '+='+canvas.height,{
duration: speed,
abort: function(){
var returnedBool;
//Pause is pressed, simulation pauses
if(pause){
//stuff
}
//restart required
if(restart){
//stuff
}
//option here to save data?
return returnedBool;
},//end abort callback
onChange: canvas.renderAll.bind(canvas),
onComplete: function(){
//stuff
}
});//end animate callback
};
What I'd like to do is call certain other functions while the above animation is active.
I know how I'd like to do this using Java or C++ but I can't seem to call the function.
I have a display panel that should output various information - as the animation completes variables will change and I'd like them to be output on the screen and be updated as they change.
I'm testing with a simple function:
function updateInfoPanel(testSpeed){
var test = document.getElementById('currentSpeed');
test.innerHTML = test.innerHTML + testSpeed;
}
I think I need to call this in the main animate loop, if I call it before it's fine but obviously only displays the variable once. Could someone tell me where I'm going wrong please?
I also have a basic collision detection function that works but not within the animate loop and I think these problems are linked.
Many thanks for any help.
I removed the unnecessary parts of the code to save space.
So I've just tried to use a function as the result of onChange:
onChange: function(){
canvas.renderAll.bind(canvas);
updateInfoPanel(speed);
},
This doesn't present syntax errors but the animation no lingers runs although the speed variable does update in the info panel very quickly. :(

You need to call renderAll(), bind is actually returning a function to be used as a handler. Just try this code:
onChange: function(){
canvas.renderAll();//.bind(canvas);
updateInfoPanel(speed);
},
And let me know if it helped?

Related

Triggering a method after every element in a .each() loop has been processed

You'll have to forgive me if I show any kind of ineptitude here, jquery and java isn't my usual area of work. But here goes:
I have a page that shows a random list of items that are pulled from a server using an API call. The idea is that every time the user clicks "generate" a new list is produced and inserted into the page. This works but it's very fast and all the user sees is a list rapidly changing. To spruce things up I've decided to put some nice animations and effects in.
I've got a jquery function that loops through each element in the list of child elements and toggles the css style of the child element so that an effect from the animate.css library is applied. The problem is when I have another function that loads the new list and this is called immediately and therefore all of the css transitions are ignored; or rather they don't get a chance to run because the second method immediately triggers.
I've tried using a callback and had no joy, I've tried using deferred objects. No luck at all.
Here's the code I have so far:
function removeOldContent() {
$('#removableContent > div').each(function (index) {
var elm = $(this);
setTimeout(function () {
elm.toggleClass('customAnim', function () {
$(this).remove();
});
}, index * 150);
});
}
function getList() {
var rId = $('.tab-content').find('.active').data('d-id');
var serviceUrl = '/GetRandom/GetList';
$.ajax({
type: 'POST',
url: serviceUrl,
data: {
reportId : rId
},
success: function(data) {
$('#reportContainer').html(data).fadeIn('slow');
}
});
}
Ideally I'd like to be able to let removeOldContent() finish completely, after all the timeouts have run. And then trigger getList() to update the content. I'll work on making a nice transition for the inbound data but first I just need to get this working.
Any advice or pointers would be greatly appreciated.
***** Update ******
I've made a fiddle. Not giving me the same error as my dev env but should be close enough for you to see
https://jsfiddle.net/rdt1pfhk/9/
Your problem is with the timing of events. Your removeOldContent function uses a setTimeout function which in turn animates and removes the items from the DOM. Your getList() function was executing before the other function had finished. I put a quick untidy solution together using your fiddle. I return a jquery deferred object from you removeOldContent method and then only call the getList when that is resolved (and the older items removed from the dom). It is not the neatest but it will point you in the right direction. I updated your fiddle here: https://jsfiddle.net/rdt1pfhk/16/
function removeOldContent() {
var deferred = new jQuery.Deferred();
....
return deferred;
}
$(document).on('click', '.quickPick', function (e) {
removeOldContent().then(function(){
getList();
});
});
If I understood correctly you need to only delay some functions for as long as you need. I think the answer you are looking for could be found here:
How to delay calling of javascript function?
Also, I'd like to mention that I don't see a
$(document).ready(function () {
//your code here
});
anywhere. Maybe I'm wrong but since you mentioned that CSS is ignored, are you sure the page is loaded before your code starts being executed?

stopping a nested javascript function running more than once

I'm trying to get a javascript function to run only once. I've seen this question has been asked before, e.g. Function in javascript that can be called only once, but I can't get the solutions in here to work. I'm not sure if it's because I've got nested functions, or whether there's something I'm missing. Essentially, I'm trying to run a function which, when a webpage is scrolled, it:
- runs a little animation on a canvas in the header
- reduces the size of the header
- leaves it at that
But when there is any subsequent scrolling, the animation keeps re-running. Here's a summarised version of the non-working code:
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
},1000);
}
});
I've also tried declaring a global variable, setting it to "false" in a "window.onload" function, then set it to true in an if function that runs the animation (the if function running only if the variable is false), but that doesn't stop it either. Thoughts?
What you're looking for is something along the lines of listenToOnce where the listener fires the one time, but never again. This could be modified to a number of calls, but the logic is like so:
Register the listener.
Then once the listener fires, remove it.
See .off
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
// $(document).off('scroll'); // or here
},1000);
}
$(document).off('scroll'); // remove listener, you can place this in the setTimeout if you wish to make sure that the classes are added/removed
});
Don't use a time out. That is why you are getting in trouble. Declare a variable outside of your function using var, that will make it global. Your code should be inside of a check for that variable. Before executing your code the first time but inside of the check, change that variable so that the code will never run again.
Try avoid setTimeout. Almost all animation can be watched for end.
function doHeaderAnimation() {
return $('header').animate();
}
function makeHeaderSmall() {
$("header").removeClass("large").addClass("small");
}
function handleScroll(event) {
if ($(document).scrollTop() > 0) {
doHeaderAnimation().then(makeHeaderSmall);
$(document).off("scroll", handleScroll);
}
}
$(document).on("scroll", handleScroll);

Javascript skips piece of code but works fine when done with debugger

I have a really simple line of code. I have a tabstrip provided by Kendo library
i = 0;
x = 10;
while (i < x) {
var tabStrip = $("#myId").data("kendoTabStrip");
tabStrip.select(i);
i++;
}
When I go step by step using debugger everything is ok - tabStrip.select(i) method is being invoked and works perfectly. But when I run it without debugger it just behaves like there was no this line. I do not understand why, and I don't know how to solve this.
(i and x variables are just sample variables, maybe the information that the method is invoked inside the while loop is important)
var tabGroupObject = $("<div>").attr("id", "myId")
tabGroupObject = $(tabGroupObject).kendoTabStrip({
animation: {
open: {
effects: "fadeIn"
}
}
});
var tabStrip = tabGroupObject.data("kendoTabStrip");
Seems to be a synchronization issue, very common in JavaScript when dealing with Ajax calls or DOM modifications. That's why it works when you execute the code step by step giving enough time for the actions to happen.
My recommendation would be to read a little about Async JavaScript and try to implement a callback function that triggers once the animation finish its task.
Assumption:- I'm assuming you are looking for an ajax trigger event which gets fired in the browser in response to the select of the tabScript.
Solution:- If that's the case please know that browsers combines all the ajax events on an element within a set amount of time into one event to reduce the number of unwanted post calls, what you can do is try adding a delay if you want these events to be called else it would simply trigger the even which gets called on the tabSctrip.select(9) as mentioned by dfsq.

How should I make complex, sequential events in javascript

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

Unable to get setTimeout to work in javascript

I am using jquery to slide up an down a div.
For some reason setTimeout is not working (looks like a function scope issue).
Not able to figure out what is wrong with the below code.
(both functions are inside $(document).ready(function(){ } )
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000, calbck);
initiate_timeout();
function calbck(){}
});
function initiate_timeout(){
var time_out = setTimeout(function() {
$('#image_view').slideUp(1000, calbck);
},2000);
}
Indenting the source code shows that you indeed have some scoping issue. The calbck function is private to the click handler function, and is thus not visible to initiate_timeout function. Either make calbck a top-level function, or make initiate_timeout a function local to the click handler function.
$('.slider-thumb').click(function() {
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000, calbck);
initiate_timeout();
function calbck(){}
});
function initiate_timeout(){
var time_out = setTimeout(function() {
$('#image_view').slideUp(1000, calbck);
}, 2000);
}
You have some JS errors and scoping issues. Why would you ever have debugging turned off when trying to troubleshoot an error? Change your code to this:
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000);
setTimeout(function() {
$('#image_view').slideUp(1000);
},2000); // will start 2 seconds after slideDown starts (which is 1 second after it completes)
});
or even better, use the completion function of the first animation to set the timer:
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000, setTimeout(function() {
$('#image_view').slideUp(1000);
},1000)); // will stay open for 1 second before sliding up again
});
Or, even better, using jQuery's delay/queuing, you can do this:
$('.slider-thumb').click(function(){
var source = $(this).attr("src");
$('#image_view').css('background-image',"url("+source+")");
$('#image_view').slideDown(1000).delay(1000).slideUp(1000); // delay 1 sec between effects
});
The calbck you were trying to pass to slideUp was not defined in the scope you were using it (inside of initiate_timout()). It was private to your click handler.
jQuery probably has better ways to chain effects than using your own timer, but I see no reason why this code shouldn't work if it matches your HTML.
Note: if your background image wasn't already pre-cached, it may not be loaded right away when your slideDown starts.
Are you getting any errors? You're passing an undefined value as calbck to slideUp in the setTimeout function.
It works for me (with my modified organization): http://jsfiddle.net/v3cjG/1/
And when I run your exact code ( http://jsfiddle.net/v3cjG/2/ ) I get an error for "Can't find variable calbck"--the var I said you were passing without defining.

Categories