How to access a public function in Javascript - javascript

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.

Related

Wait for Async result before allowing user to go forward in UI Wizard

I am using a Wizard-UI component that has a handleChangingEvent() function that is called when a user hits the forward/back button. This function returns a boolean for whether the transition should occur. During this method call I need to make an async call to determine whether there are errors on the current Wizard step. The results determine whether i should return true/false. How can I have the handleChangingEvent() function wait until the results have arrived?
This is a tricky computer science problem that has many elaborate solutions but no simple ones in javascript. You have 2 options.
Make the call to the server synchronous, hence blocking the thread until you receive a response.
Override the click handlers on the Prev/Next buttons.
Use something like this solution to store all the current click handlers, then assign your own click handlers that do something like
var can_i_continue = false;
$('#next_btn').on('click', function () {
$.ajax('/can/i/continue', { params : 'foo' })
.then(function (event) {
can_i_continue = true;
runSavedClickHandlers(event);
});
})
wizard.handleChangingEvent = function () {
return can_i_continue;
};

Simple onClick Every Other Click Javascript HTML

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.

Pause JavaScript function

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.

Mootools - how to destroy a class instance

What I'm trying to do is create a class that I can quickly attach to links, that will fetch and display a thumbnail preview of the document being linked to. Now, I am focusing on ease of use and portability here, I want to simply add a mouseover event to links like this:
Testing
I realize there are other ways I can go about this that would solve my issue here, and I may end up having to do that, but right now my goal is to implement this as above. I don't want to manually add a mouseout event to each link, and I don't want code anywhere other than within the class (and the mouseover event creating the class instance).
The code:
TestClass = new Class({
initialize: function(anchor) {
this.anchor = $(anchor);
if(!this.anchor) return;
if(!window.zzz) window.zzz = 0;
this.id = ++window.zzz;
this.anchor.addEvent('mouseout', function() {
// i need to get a reference to this function
this.hide();
}.bind(this));
this.show();
},
show: function() {
// TODO: cool web 2.0 stuff here!
},
hide: function() {
alert(this.id);
//this.removeEvent('mouseout', ?); // need reference to the function to remove
/*** this works, but what if there are unrelated mouseout events? and the class instance still exists! ***/
//this.anchor.removeEvents('mouseout');
//delete(this); // does not work !
//this = null; // invalid assignment!
//this = undefined; // invalid assignment!
}
});
What currently happens with the above code:
1st time out: alerts 1
2nd time out: alerts 1, 2
3rd time out: alerts 1, 2, 3
etc
Desired behavior:
1st time out: alerts 1
2nd time out: alerts 2
3rd time out: alerts 3
etc
The problem is, each time I mouse over the link, I'm creating a new class instance and appending a new mouseout event for that instance. The class instance also remains in memory indefinitely.
On mouseout I need to remove the mouseout event and destroy the class instance, so on subsequent mouseovers we are starting fresh.
I could create a helper function for this to make sure that the class is only created once for each link, something like this:
function TestClassHelper(anchor) {
anchor = $(anchor);
if(!anchor) return;
if(!anchor.retrieve('TestClass')) anchor.store('TestClass', new TestClass(anchor));
anchor.retrieve('TestClass').show();
}
Testing
I may end up implementing it this way if I have to, but I'm curious as to how I can fix the other method.
This looks a lot more complex than it should be. But if you want to fix this, you need to save a reference to the bound function somewhere and later pass that to removeEvent.
For example:
// inside initialize
this.boundHandler = function() {
this.hide();
}.bind(this)
this.anchor.addEvent('mouseout', this.boundHandler);
// inside hide
this.removeEvent('mouseout', this.boundHandler);
See the removeEvent docs for examples of this very issue.
I wouldn't recommend event delegation here either for performance reasons. The best approach is to attach the handler in code rather than inline and only do it once, so unnecessary objects are not being created each time the user mouses over.

How can I chain click events with Mootools so that they execute in order?

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

Categories

Resources