I need to pause a JavaScript function execution in the middle and then resume it after a button click. Please help me.
This isn't possible.
Break the function up in to two parts, run one, and have the other assigned to the click event handler of the button.
You could use a pop up box.
alert("Pausing to get Coffee");
Like David said, it is not possible to stop execution of a function in Javascript (well, not at the moment anyway). One solution would be this :
** EDITED ** after you added some precision to what you wanted to do
// pass the reference of the button id to the function
function showConfirm(message, callback) {
// 1. check if the lightbox is not already created, if not create it
// 2. keep a reference to your key elements, for example, your buttons
var btnOk = document.getElementById('btnOk'); // ...for example
var btnCancel = document.getElementById('btnCancel'); // ...for example
// 3. have a 'cleanup' function so you can dismiss your lightbox, unregister
// any events/callbacks, etc.
var cleanup = function() {
// 6. hide lightbox
// 7. remove events/callbacks, etc.
btnOk.click = null; // for example
btnCancel.click = null; // for example
// etc.
};
// 4. update your lightbox with the message given in argument
// 5. register some events to your buttons...
btnOk.click = function() {
callback(true); // ok was pressed
cleanup();
};
btnCancel.click = function() {
callback(false); // cancel was pressed
cleanup();
}
}
All you have to remember is that, in Javascript, everything should be asynchronous. If your function should return a value, it should be a function that does not require long to execute. As soon as you read "user input" with Javascript, you need callbacks. You might want to take a look at how other lightbox implementations are done, especially in frameworks like JQuery, etc.
Related
I'm not a JavaScript guy, so I'm not sure how to get this working.
I'm using SmartWizard in one of my projects. The original SmartWizard code was extended by someone that is no longer available and is not around to ask.
What I want to do is to leave his code in place as it is and to just access the functions within his class to move the user forward or back in the wizard process.
As far as I can tell, the functions that perform the actions I need are called goForward and goBackward. How to access them though from outside his class?
Here is the goForward function:
SmartWizard.prototype.goForward = function(){
var nextStepIdx = this.curStepIdx + 1;
if (this.steps.length <= nextStepIdx){
if (! this.options.cycleSteps){
return false;
}
nextStepIdx = 0;
}
_loadContent(this, nextStepIdx);
};
There are also other functions within his code that I would like to access such as 3 callbacks that can be triggered when the user clicks the Next, Prev and Finish buttons. Below are those 3 callbacks that I need to access.
$.fn.smartWizard.defaults = {
onLeaveStep: null, // triggers when leaving a step
onShowStep: null, // triggers when showing a step
onFinish: null, // triggers when Finish button is clicked
};
Can someone shed some light on what I need to do here to access them?
$('#wizard').smartWizard({
...
...
// Events
onLeaveStep: function () {
// Do Your stuff on step leave
}, // triggers when leaving a step
onShowStep: : function () {
// Do Your stuff on step show
}, // triggers when showing a step
onFinish: : function () {
// Do Your stuff on finish
} // triggers when Finish button is clicked
});
These methods looks like instance methods, not class methods.
For example, goForward is referencing instance variables (e.g., curStepIdx, steps), so you cannot call it as a class method. You'll need to call it on an instantiated object.
onclick="HandleAction(\'playnow\');HandleAction(\'stop\');"
Performs the two functions simultaneously. No good since it just does the action \playnow\ and then \stop\ immediately after.
I need onClick to perform the function every other click.
Click 1: \playnow\
Click 2: \stop\
Click 3: \playnow\
etc.
Is there a simple way to achieve this?
Define a var which holds the current state:
var state = false;
function handleAction() {
if (state == false) {
// stuff for 'playnow' action
state = true;
return;
}
if (state == true) {
// stuff for 'stop' action
state = false;
return;
}
}
Declare a global variable and interchange it depending on what is passed:
var switch = "playnow";
function handleAction(a) {
if a === switch {
alert(a);
}
else {
alert(a);
switch = a;
}
}
See my answer to this question.
Lets make a function called toggle that lets you specify actions that happen every other click
var toggle = function (a, b) {
var togg = false;
return function () {
// passes return value back to caller
return (togg = !togg) ? a() : b();
};
};
We would then setup our click handler like this
button.addEventListener('click', toggle(function () {
//play now logic
}, function () {
// stop logic
}));
Your click handler now alternates between the first and second functions with every click.
Yes. You could do something like
var play=false,
button = document.getElementById('playbtn'),
audiotag = document.getElementById('audio');
button.addEventListener('click', function(){
if(play){
audiotag.pause()
play=true;
}else{
audiotag.play();
play=false;
}
})
to make this work you could use html like this:
<div id="audioplayer">
<button id="playbtn">play</button>
<audio id="audiotag" src="path/to/audiofile"></audio>
</div>
so you would add the audio html like above and use getElementById to get each element in javascript. after that you attach an event listener to listen for the click event and call the handler which is the anonymous function. Inside that function, you can use the native play and pause methods directly on the audio object to stop audio when it's playing and then play it again when it's stopped.
there are other attributes you can add to the audio tag to start it playing as soon as the page loads. When you click the button, the play variable is set to true so it will pause on the first click and then sets that to false. A subsequent click will play it again and set the variable to true again and so on
Tyr gave you an answer how to solve your problem. Here you go some notes which can help you design better code.
If you have i.e. one big animation and one button on your web, your code is perfectly ok, to keep the code simple is a good idea. But if you have something like this
<button onclick="...">Animation A</button>
<button onclick="...">Animation B</button>
Then you need better design. If you insert state global var into HandleAction, you break low coupling, HandleAction is bound to your single event and can't be reused elsewhere.
It is good to ask yourself What does this function do? In the first case, it is useful to choose better name, like HandleIntroAnimation. If it handles (any) animation, then it is good to specify it in the parameter.
function HandleAnimation(animation, play) {
if(play) ... // playnow
else ... // stop
}
This indeed does what the name tells. To use it in your code, write a proxy function:
<script>
// If you enhance your web, you only alter this code.
// HandleAnimation stays the same, regardless the changes.
var animationStatus = {}
function ToggleAnimation(animation) {
animationStatus[animation] = !animationStatus[animation];
HandleAnimation(animation, animationStatus[animation]);
}
</script>
<button onclick="toggleAnimation(this)">Animation A</button>
<button onclick="toggleAnimation(this)">Animation B</button>
Finally, you could completely decouple HTML and JS:
animations.js
window.addEventListener("load",function() {
// HandleAnimation and ToggleAnimation definitions goes here
// to avoid collisions in global namespace
var anims = document.getElementsByClassName("animation");
for(var i=0; i<anims.length; ++i) anims[i].addEventListener("click",function() {
ToggleAnimation(anims[i]);
});
});
your html
<script src="animations.js"></script>
<button class="animation">Animation A</button>
<button class="animation">Animation B</button>
and you have animation framework: every element with animation class magically toggles its animation. The animation data could be provided in data-* attribute, data-animation in this case.
Then you can provide it as open-source on github or use someone elses open code to fill the missing parts in your code where you were too lazy to code it yourself. Since many wheels were already invented, the only thing you need to code is usually proxy functions. That's how coders save each others time. Happy coding.
I have a search box on my web page that has check boxes in order for the user to filter their results. Only one check box can be checked at once.
When a check box is clicked my code runs off and applies the filter to the list and returns the correct results.
The problem I have is that when a check box is clicked multiple times in quick succession, it queues the requests and pulls them back one by one. This can take a while if a check box is checked and then un-checked multiple times.
Is there any way in Javascript to inform the function that it has been called again and it should stop everything other than this last request?
You want to wrap your onclick callback in a debouncing function like
http://underscorejs.org/#debounce
Say you have this
function search() {
// ...
}
$jquery(".myFilterCheckboxes").click(search);
You should be able to just change the above to:
// Only allow one click event / search every 500ms:
$jquery(".myFilterCheckboxes").click(_.debounce(search, 500));
There are tons of debouncing functions out there, and writing your own isn't a big deal really if you can't or don't want to include underscore.js.
My first thought was towards debouncing because you mentioned multiple clicks creating multiple events in a short period. Debouncing is used really often for things like type-ahead search or autocomplete to provide a little space between key presses for thinking time.
As others have mentioned it may make more sense to simply disable the checkboxes / click event while your search is running. In that case, try something like this:
function disableClick(elem) {
elem.unbind("click");
elem.attr("disabled", true);
}
function enableClick(elem, onclick) {
// Enable click events again
elem.live("click", search);
// Enable the checkboxes
elem.removeAttr("disabled");
}
function search() {
var boxes = $jquery(".myFilterCheckboxes");
disableClick(boxes);
$.get(...).always(function() {
enableClick(boxes, search);
});
}
$jquery(".myFilterCheckboxes").live("click", search);
Why disable the click event, add the disabled attribute to the checkboxes instead of just a global lock variable? Well, global locks can be somewhat error prone, but more than that, we already have a global object that matters in the DOM. If we just modify the DOM state we get the right behavior and signal to our users that they should chill out on the checkboxes until the search completes.
That said, it probably makes sense with any kind of locking / unbinding scenario to indicate to the user with a loading spinner or something that you're doing work.
You can use a lock pattern:
http://jsfiddle.net/RU6gL/
HTML
<input type="checkbox" onclick="fire()" >CB1
<br />
<input type="checkbox" onclick="fire()" >CB2
JS
function_lock = false
fire = function() {
// First, check the lock isn't already reserved. If it is, leave immediately.
if (function_lock) return;
// We got past the lock check, so immediately lock the function to
// stop others entering
function_lock = true;
console.log("This message will appear once, until the lock is released")
// Do your work. I use a simple Timeout. It could be an Ajax call.
window.setTimeout(function() {
// When the work finishes (eg Ajax onSuccess), release the lock.
function_lock = false;
}, 2000);
}
In this example, the function will only run once, no matter how many times the checkboxes are clicked, until the lock is released after 2 seconds by the timeout.
This pattern is quite nice, because it gives you control opver when you release the lock, rather than relying on a timed interval like 'debounce'. For example, it will work with Ajax. If your checkbox is triggering an Ajax call to do the filtering, you can:
On first click, set the lock
Call the Ajax endpoint. Subsequent clicks won't call the Ajax endpoint.
In the Ajax success function, reset the lock.
The checkboxes can now be clicked again.
HTML
<input type="checkbox" onclick="doAjax()" >CB2
JS
ajax_lock = false
doAjax: function() {
// Check the lock.
if (ajax_lock) return;
// Acquire the lock.
ajax_lock = true;
// Do the work.
$.get("url to ajax endpoint", function() {
// This is the success function: release the lock
ajax_lock = false;
});
}
The issue here is that the checkbox is repeatedly clicked on. You should instead disable your checkbox(which would also disable the click event on the element) when you are processing and then re-enable your checkbox when you're done processing.
The debouncing is a great idea, but you don't always know how long it will take for your processing function to finish.
Here's a simple example using jquery promise to re-enable the checkbox after some processing
http://jsfiddle.net/94coc8sd/
with the following code:
function processStuff() {
var dfd = $.Deferred();
// do some processing, when finished,
// resolve the deferred object
window.setTimeout(function(){
dfd.resolve();
}, 2000);
return dfd.promise();
}
function startProcessing() {
$('#processingCheckbox').attr('disabled', 'disabled');
var promise = processStuff();
promise.done(enableCheckbox);
}
function enableCheckbox() {
$('#processingCheckbox').removeAttr('disabled');
}
$('#processingCheckbox').on('click', startProcessing);
How can I return a parent function from a child function on click? Here's an example code:
function returnFunction() {
var output = false;
$('a').click(function() {
output = true;
});
return output;
}
var result = returnFunction();
The result will always be false since the click hasn't happened at the time the code is being run. How could I make this work though?
My intention is to call pop-up dialogs, and I'd like to have all the logic inside one function that can easily be loaded - including the click events of a Confirm dialog box.
Elsewhere in scripts I'd be calling it this way for example:
// Menu click triggers the dialog
$('a').click(function(e) {
// The function displays the dialog and returns the click events of the dialog
var result = returnFunction();
// If result was false, we'll prevent the menu access, for example
if (!result) {
e.preventDefault();
}
});
I'm aware of the jQuery UI dialog plugin. But I'd like to achieve this without it for now.
Thanks.
An over-simplification of it is:
Everything stops (including scrolling and clicking on hyperlinks) while executing javascript. This means you cannot "pause" the script until someone clicks on a link.
The typical way of solving this is a callback function:
function my_callback(some, arguments) {
// you can do whatever in here: an ajax load, set a global variable, change some of the page, ...
console.log(some, arguments);
alert(some + " " + arguments);
}
function returnFunction(callback, param) {
var output = false;
$('a').click(function() {
callback(param, "world");
});
}
returnFunction(my_callback, "hello");
demo at http://jsfiddle.net/UnBj5/
EDIT:
I did mention global variables because they are an option, but they are typically bad style. Try to use other means if possible.
If you want more help with it, provide more details of what you are trying to do.
Try using parameters instead. Send a parameter to a function that shows your alert boxes, show a different pop-up alert depending on the parameter, what you are trying to do won't work because its basically a chicken-egg problem.
I have a series of buttons that fire the list function when they are clicked. The list function itself contains an AJAX request and a bunch of other stuff before and after which loads in a separate section of the page.
var list = function() { }
$$('.buttons').addEvent('click', list);
Everything works fine if I wait for list to complete before clicking on another button. However, if I click on two buttons quickly, the page will start to return incorrect responses. In fact, it appears as though the responses get out of sync by 1. So if I quickly click on button A then button B, it will load response A. Then if I click (much later) on button C, it will load response B.
There are two ways I can see to solve this:
1) Remove the click event from other buttons when any button is clicked and then restore it when list is complete. Unfortunately, I have tried placing $$('.buttons').removeEvents() at the top of the list function and then $$('.buttons').addEvent('click', list); at the bottom but this has no effect on the problem.
2) Chain the click events so that list is only ever executed when the preceding list has finished.
So can anybody tell me how to get the second solution working? Additionally, if anybody knows why the first solution doesn't work and why I get the weird delayed AJAX response behaviour, that would be great!
The first solution doesn't work because events on an element are fired in order, but are executed asynchronously. You'll need to setup a queue of callbacks that you can process when the event is triggered.
Here's the basic idea:
addQueuedEvent = function(node, event, callback) {
if ( typeof callback != "function" ) {
throw "Callback must be a function";
}
event = event.toLowerCase();
var eventQueue = "_" + event;
if ( !node.hasOwnProperty(eventQueue) ) {
node[eventQueue] = [];
}
node[eventQueue].push(callback)
};
processEventQueue = function(node, event) {
var eventQueue = "_" + event;
if ( node.hasOwnProperty(eventQueue) ) {
for ( var i=0, l=node[eventQueue].length; i<l; ++i ) {
node[eventQueue][i]();
}
}
};
And the usage:
var someElement = $("#some-element");
addQueuedEvent(someElement, "click", callback1);
addQueuedEvent(someElement, "click", callback2);
addQueuedEvent(someElement, "click", callback3);
someElement.addEvent("click", function() {
processEventQueue(this, "click");
});
The syntax checks out, but this is not tested. Hope that helps.
i would personally just set a global / scoped variable in your class or whatever - something like 'isClicked = false'.
then simply check at the the the click event function, something like:
var isClicked = false, click = function() {
if (isClicked)
return false;
isClicked = true;
// ... do stuff, chained or otherwise...
// when done, make click function work again:
isClicked = false; // you can do this onComplete on the fx class also if you use it
};
i would go against chaining events with effects - if you have an animation going on, simply wait for it to end--otherwise it can get messy for any trigger happy user that thinks double clicking is the way to go. an alternative is to stop / cancel any effects that are taking place on a new click. for instance, you can stop any tweens etc through FX by something like:
if (isClicked === true) fxinstance.cancel();
http://mootools.net/docs/core/Fx/Fx
the other thing you can do is look at the mootools .chain class
http://mootools.net/docs/core/Class/Class.Extras#Chain
and also, on any fx instances, you can pass on link: "chain" and simply queue them up.
good luck