Common callback for all instances of a class in Javascript - javascript

I've got a custom Javascript class that generates a JQuery mousedown callback. The mousedown callback is on $(document) and should really only be set for the first new instance, and not for any subsequent ones. I've got something like the following:
function myclass(arg){
this.arg = arg;
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
I'd like those callbacks to only register once in the event that multiple myclass instances are crated, and not at all if none are created.

You should use a variable to flag if the events have already been registered and register them only if they haven't already been registered.
An example of this:
var registered = false; // the 'flag' variable, default to false (i.e. not yet registered)
function myclass(arg){
this.arg = arg;
if (!registered) { // check the 'flag' variable if events have been registered yet
registered = true; // set the 'flag' variable as events will be registered this time
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
}

There's a jQuery function for that. Use .one() to bind a handler to the first instance of an event raised on an element (in this case document).
function myclass(arg){
this.arg = arg;
$(document)
.one('mousedown.yourEvent', downHandler)
.one('mouseup.yourEvent', upHandler);
}
function downHandler(e) {
// start action
}
function upHandler(e) {
// stop action
//ensure event dead forever
$(document).on('.yourEvent', function() { return false; });
}
Updated. The changes (using named handlers rather than anonymous functions, putting events in a specific namespace) are to ensure that new instances of myclass don't rebind to the event if they are created after the first one has finished being unbound.

Here are a couple of possible options.
Option 1: A global var
function myclass(arg){
this.arg = arg;
if (!window._myClassCreatedAlready) {
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
window._myClassCreatedAlready = true;
}
Option 2: jQuery Data
function myclass(arg){
this.arg = arg;
if (!$.data(document, "mousedownset")) {
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
$.data(document, "mousedownset", true);
}

Related

understanding event handling and callbacks in javascript

I was messing around with IndexedDB and I realised that I don't really get event handling in JavaScript.
So here's the code:
var request = indexeddb.open(bla, version);
request.onsuccess = function (event) { };
So the open-method returns a IDBOpenDBRequest object, which, according to Mozillas site, inherits from IDBRequest, which apart from properties and methods also has event handlers, one of them being onsuccess:
https://developer.mozilla.org/en-US/docs/Web/API/IDBRequest.onsuccess
So on the mozilla site, onsuccess is just function () { }
Now, when the database was opened sucessfully, the "onsuccess" event fires and the appropiate event handler is called, in this case the function that I defined. But how exactly does that happen?
The request variable contains an instance of the IDBOpenDBRequest. So when I write request.onsuccess = somefunction(), am I overwriting the default function of the IDBRequest-class?
I dont get why I can write request.onsuccess = somefunction(event) { } and how the event is passed to that function.
EDIT:
function myObect() {
this.open = function(a,b,c) {
if (c > 20) {
this.success("String");
}
};
};
var myrequest = new myObect();
myrequest.open(4,2,21);
myrequest.success = function (ev) {
console.log(ev);
};
To create a similar api, you can do something like:
function open(a, b, c) {
var request = {};
if(c > 20) {
setTimeout(function() {
if(typeof request.success === "function") {
request.success("String");
}
}, 1);
}
return request;
}
var myrequest = open(4, 2, 21);
myrequest.success = function(ev) {
console.log(ev);
};
Here, setTimeout is asynchronous so the callback function is not executed immediately. When any asynchronous task is run in JavaScript, the currently executing code will run to completion before any callback is called. So success is guaranteed to be set before request.success called.
The Indexed DB open call similarly runs an asynchronous task, and then dispatches events when it is finished which will eventually call your callback function.
I overwriting the default function of the IDBRequest-class
Looks like there is no default behavior, so you just set up your own func.

OOP Javascript Callback

I've really been having trouble grasping this concept and thought if I saw it on a little bit of my own code it might click. I'd really like to take advantage of callback functions while still keeping an object oriented approach. Thank you for any help you can offer!
//adds functionality to buttons
addClickEvent(newDataCollect,function() {addClickEvent(dataSubmitBtn, function(){testAjax(dataForm.elements);});
function addClickEvent(elem,click,addtl) {
var nwClickEvent = new elemEvents(elem,click,addtl);
nwClickEvent.onClick();
}
//add click event object & properties
function elemEvents(elem,click,addtl) {
this.elem = elem;
this.click = click;
this.addtl = addtl;
}
//add click event object method
elemEvents.prototype = {
onClick: function() {this.elem.onclick = this.click;}
}
Yes, I would say you are using a callback already:
//add click event object & properties (constructors should be PascalCase)
function ElemEvents(elem,click,addtl) {
this.elem = elem;
// `click` should be a function, and as such, a callback that is called when the element is clicked
this.elem.onclick = click;
this.addtl = addtl;
}
// later:
var e = new ElemEvents(
// the `elem`
document.getElementById("id"),
// this is the callback.
function (event) {
console.log("hi from event: " + event);
},
// the `addtl`.
"");
One thing to keep in mind here is that callbacks are not necessarily asynchronous. In this particular case, you are defining an onClick handler which will run once the user clicks on something, at some point in the future. This is async - but consider the following:
function log(/* .. */){ // can be any arguments, we're just .apply'ing log with arguments
console.log.apply(console, arguments)
}
function logThen(done /* .. */) { // takes a callback as first argument, then vals to log
var args = [].splice.call(arguments, 1); // calls splice on the arguments, removing the CB, and returning whatever you had left
console.log.apply(console, args);
if (done && typeof done === 'function'){
done();
}
}
log(1);
log(2);
log(3);
logThen(alert, 4);
log(5);
log(6);
The output will be:
1
2
3
4 // shows an alert, then continues with..
5
6
This is all synchronous code - there is nothing that is pushed onto the next call stack, so it executes everything, including your callback (in this case alert) synchronously.
Just a little heads up!

How can I only run an AJAX call on change when the mouse (or finger) is no longer dragging?

I have series of interactive sliders that change calculated values.
The calculations run on every tiny move of a dragged handle (via mousedown or touch drag event).
I need to update a database with the values but would prefer only to grab the values after the user "drops" the handle.
How can I determine if a finger or mouse is down, in order to skip the AJAX call?
function handleIsBeingDragged() {
// calculations based on all the input values here
// pseudo-code to check for mouseup event
if (mouseUp) {
// save only if mouse is now up - to avoid hundreds of updates per drag event
$.ajax();
}
}
You need to add a bit of hysteresis to your code.
It happens I wrote a generic debounce function for another answer here on SO which would be useful for this.
Here's how you'd use it:
function saveTheData() {
$.ajax(); ///
}
var saveTheDataDebounced = debounce(50, saveTheData);
function handleIsBeingDragged() {
saveTheDataDebounced();
}
The debounce function:
// debounce - debounces a function call
//
// Usage: var f = debounce([guardTime, ] func);
//
// Where `guardTime` is the interval during which to suppress
// repeated calls, and `func` in the function to call.
// You use the returned function instead of `func` to get
// debouncing;
//
// Example: Debouncing a jQuery `click` event so if it happens
// more than once within a second (1,000ms), subsequent ones
// are ignored:
//
// $("selector").on("click", debounce(1000, function(e) {
// // Click occurred, but not within 1000ms of previous
// });
//
// Both `this` and arguments are passed through.
function debounce(guardTime, func) {
var last = 0;
if (typeof guardTime === "function") {
func = guardTime;
guardTime = 100;
}
if (!guardTime) {
throw "No function given to debounce";
}
if (!func) {
throw "No func given to debounce";
}
return function() {
var now = +new Date();
if (!last || (now - last) > guardTime) {
last = now;
return func.apply(this, arguments);
}
};
}
You could assign the AJAX call to the "mouseup" event instead. In jQuery mobile "vmouseup".

How to determine if a function has been called without setting global variable

I am looking for a good technique to get away from what I am tempted to do: to set a global variable.
The first time someone runs a function by clicking a button it triggers an initial function to turn a few things into draggables. Later, if they click the button a second time I want to determine if the init function has been initialized, and if so to not call it again. I could easily do this by setting a global variable from the init function and then checking that variable from the click function, but I'm wondering how to do this without setting a global variable. I would really like an example of a way to do this.
You could add a property to the function:
function init() {
init.called = true;
}
init();
if(init.called) {
//stuff
}
While #Levi's answer ought to work just fine, I would like to present another option. You would over write the init function to do nothing once it has been called.
var init = function () {
// do the initializing
init = function() {
return false;
}
};
The function when called the first time will do the init. It will then immediately overwrite itself to return false the next time its called. The second time the function is called, the function body will only contain return false.
For more reading: http://www.ericfeminella.com/blog/2011/11/19/function-overwriting-in-javascript/
Why don't you just check to see if your draggables have a class of draggable on them?
if ($('.mydiv').is('.draggable')) {
//do something
}
Function.prototype.fired = false;
function myfunc() {
myfunc.fired = true;
// your stuff
};
console.log(myfunc.fired) // false
myfunc();
console.log(myfunc.fired) // true
What you could do is unhook the init function from the prototype.
​var Obj = function () {
this.init = function () {
document.write("init called<br/>");
this.init = null;
}
}
var o = new Obj();
if (o.init) document.write("exists!<br/>");
o.init();
if (o.init) document.write("exists!<br/>");
o.init();
​
The first if will be true and print exists! but since the function removes itself, the second if will fail. In my example, I call the second init unconditionally just to show that nothing will happen, but of course you could call it only if it exists:
if (o.init) o.init();
http://jsfiddle.net/coreyog/Wd3Q2/
The correct approach is to use the Javascript Proxy APIs to trap the function calls using apply handler.
const initFun = (args) => {
console.log('args', args);
}
const init = new Proxy(initFun, {
apply(target, thisArg, args){
target.calls = target.calls ? target.calls + 1 : 1;
return target.apply(thisArg, args);
}
});
init('hi');
console.log(init.calls); // 1
init('hello');
console.log(init.calls); // 2

JavaScript eventhandler: problem with execution of handler that got parameters

I have this problem:
I have defined an event handler which requires parameters on it.
var handler = function(p1, p2){
//some code
}
Then I add the event handler to an object inside a function.
function foo(p1, p2){
//Some code
obj.addEventListener('click', handler(p1, p2), false)
}
As you already know the code above is not correct. It wont listen to the event. Instead it will execute the function instantly. Now to fix this I can just erase handler(p1, p2) and insert function(){ handler(p1, p2) } instead. But the problem is that I have an another function that I want to remove the event listener, which is not possible with the latter solution.
function koo(){
//Some code
obj.removeEventListener('click', handler, false)
}
How do I fix this?
I think you'll need to create a closure:
var createHandler = function(p1, p2){
return function (event) {
//some code that uses p1 and p2
};
};
var handler;
...and now you can assign the handler like so, while still having access to p1 and p2:
function foo(p1, p2){
handler = createHandler(p1, p2);
//Some code
obj.addEventListener('click', handler, false);
}
function koo(){
//Some code
obj.removeEventListener('click', handler, false);
handler = null;
}
Note that handler is now a global variable.
Update: I just realized that in your case, this could be simplified by merging createHandler and foo as follows:
var handler; // we need this to be accessible both to foo and koo
function foo(p1, p2){
handler = function(event) {
// some code that uses p1 and p2
};
//Some code
obj.addEventListener('click', handler, false);
}
function koo(){
//Some code
obj.removeEventListener('click', handler, false);
handler = null;
}
I don't think you want to pass arguments there - because it is a callback, and you are never sure what those variables will be.
Can you do...
(function() {
var p1 = 'a',
p2 = 'b',
obj = document.getElementById('my-object');
var handleClick = function(event) {
// Access to p1 and p2
// Access to `event` object containing info about the event
}
obj.addEventListener('click', handleClick, false);
// If you want to remove it
obj.removeEventListener('click', handleClick, false);
})();
May I ask why you want to have arguments on it? Do you intend to call it from a non click triggered way as well?
Isn't it just this:
function foo(){
//Some code
obj.addEventListener('click', handler, false)
}
Pass the function instead of calling it.

Categories

Resources