Change reference on existing event - javascript

I have the following JavaScript code:
var MyGlobalRef = (function () {
function init(obj1, obj2) {
prepareEvents(obj1);
prepareEvents(obj2);
function prepareEvents(obj) {
var handleMouseUp = function (evt) {
// do work with obj
};
obj.addEventListener('mouseup', handleMouseUp);
}
}
return {
init: init
}
})();
In my main page, I'm frequently calling the init function like this:
function moveNext(){
MyGlobalRef.init(getNewObj1(), getNewObj2());
}
My problem is that on that moveNext() function, I tend to replace the existing objects, meaning that there are only 2 objects in the page at all times. However, the more I call moveNext the more event listeners get generated. So by the time I moveNext 3 times, the mouse up event fires 3 times per one mouse up. The obvious solution is to call addEventListener only the first time. However, the problem with this is that obj (that is used in the mouse up event) does not update and still references the original obj. Also, I failed to use removeEventListener because I don't have a reference to the obj once it's initiated.

To expand on my comment. Here is what I envision. You return a destroy function as a result of init execution
Solution #1
var MyGlobalRef = (function () {
function init(obj1, obj2) {
prepareEvents(obj1);
prepareEvents(obj2);
function prepareEvents(obj) {
var handleMouseUp = function (evt) {
// do work with obj
};
obj.addEventListener('mouseup', handleMouseUp);
}
return function() {
obj1.removeEventListener('mouseup');
obj2.removeEventListener('mouseup');
};
}
return {
init: init
}
})();
// cleanup is a variable stored in your code to run a cleanup on obj1 and obj2 later on
function moveNext(){
if (cleanup) {
cleanup();
}
cleanup = MyGlobalRef.init(GetNewObj1A(), GetNewObj1A());
}
Solution #2
Keep reference to obj1 and obj2 in closure.
var MyGlobalRef = (function () {
var _obj1;
var _obj2;
function init(obj1, obj2) {
prepareEvents(obj1);
prepareEvents(obj2);
function prepareEvents(obj) {
var handleMouseUp = function (evt) {
// do work with obj
};
obj.addEventListener('mouseup', handleMouseUp);
}
_obj1 = obj1;
_obj2 = obj2;
}
function cleanup() {
if (_obj1) {
_obj1.removeEventListener('mouseup');
}
if (_obj2) {
_obj2.removeEventListener('mouseup');
}
}
return {
init: init,
cleanup: cleanup
}
})();
function moveNext(){
MyGlobalRef.cleanup();
MyGlobalRef.init(GetNewObj1A(), GetNewObj1A());
}

You call prepareEvents before the function, put function prepareEvents(obj) before your init function. Try it.

Related

how to define eventEmitter properly in javascript

I need to define a helper object in which I need a function that will execute on each "orientationchange" event of window.
My code as below and it is not in correct form. Can you please help me how can I define onRotate properly so that I can use it globally.
<script type="text/javascript">
'use strict';
var GlobalHelper = (function () {
var me = {
onRotate: onRotate // this is where I am struggling
}
function onRotate() {
window.addEventListener("orientationchange", function (event) {
console.log(event.target.screen.orientation.angle);
});
}
return me;
})();
GlobalHelper.onRotate = function (e) {
console.log(e);
}
</script>
I found an answer to my own question. It was actually pretty easy.
<script type="text/javascript">
'use strict';
var GlobalHelper = (function () {
var me = {
onRotate: function (e) { }
}
function _init() {
window.addEventListener("orientationchange", function (event) {
me.onRotate(event);
});
}
_init();
return me;
})();
GlobalHelper.onRotate = function (e) {
console.log(e);
}
</script>
Another option is to add custom programmable method for creating listeners and bonded functions. You can test it by Running code snippet and pressing keyboard keys while focused on results window.
// Set app object
const bindFn = e => {
// Set object to hold your functions
let fns = {};
// Set event listener to run all functions in fns object
window.addEventListener(e, event => {
for(const f in fns) fns[f](event);
});
// Return method to set new functions
// You can extend it with another function for
// deleting or altering, as you wish...
return {
add: (name, fn) => {
if(!fns[name]) fns[name] = fn;
else console.log(`Function with name ${name} already exist, skipping...`);
}
};
};
// Set binder with proper event
// here we do orientation change
const OrCh = bindFn("orientationchange");
// Add some function to execute on this event
OrCh.add('log', event => console.log(event.target.screen.orientation.angle));
//
// Test with another event that easy to trigger on stackoverflow
const KeyP = bindFn("keypress");
// Add logger
KeyP.add('log', event => console.log(`key pressed: ${event.code}`));
// Add something else
KeyP.add('logAlt', event => console.log(`Alternative function: ${event.code}`));

Use of debounce on Ext 3.4 framework

I want to implement the debounce function on Ext.Button, so I extended it and override the onClick function, like this:
MyButton = Ext.extend(Ext.Button, {
onClick: function(e) {
var that = this;
var args = e;
clearTimeout(this.timeoutDebounce);
this.timeoutDebounce = setTimeout(function(){
MyButton.superclass.onClick.apply(that, [args])
}, this.debounce);
}
});
Debounce is a parameter passed on the x-type declaration.
The problem here is that the "args" parameter I'm passing to onClick has changed when it's called from "click" to "mouvemove" and it doesn't fire the events it should.
Is there a way to record the "e" parameter received in the function to pass to onClick on superclass?
The function passed to setTimeout must be wrapped in order to keep the value presented in current scope:
function createCallback(args) {
return function() {
MyButton.superclass.onClick.apply(that, [args]);
}
}
Also, e is passed by reference, so you need to create a copy of it. Using ExtJS, you can use Ext.apply method:
Ext.apply({}, e);
The full code should be:
var MyButton = Ext.extend(Ext.Button, {
onClick: function(e) {
var that = this;
function createCallback(args) {
return function() {
MyButton.superclass.onClick.apply(that, [args]);
// you can also use call since you know the arguments:
// MyButton.superclass.onClick.call(that, args);
}
}
clearTimeout(this.timeoutDebounce);
var copy = Ext.apply({}, e);
this.timeoutDebounce = setTimeout(createCallback(copy), this.debounce);
}
});
You should clone the object:
var args = Ext.apply({}, e);
this.timeoutDebounce = setTimeout((function(args){
return function(){MyButton.superclass.onClick.apply(that, [args])};
})(args), this.debounce);

How do I call a nested JavaScript function properly

I'm trying to set up function a nested function that I can call throughout my script, but I keep getting "error undefined is not a function". Perhaps someone can help me with how to do this correctly.
First I set global my variables:
var trigger = document.getElementById('trigger');
var subject = document.getElementById('subject');
Then I create a show/hide function:
var toggleVis = function() {
function showSomething() {
trigger.classList.add("active");
subject.classList.add("active");
}
function hideSomething() {
trigger.classList.remove("active");;
subject.classList.remove("active");
}
}
Then I set my event listener:
trigger.addEventListener('click', function() {
if ( subject.classList.contains("active") ) {
toggleVis.hideSomething();
}
else {
togglePicker.showPicker();
}
});
The reason I'm trying to do it this way is that there will be other triggers for subject on the page that will need access to the show/hide functions.
You can't access the functions inside the function, they are out of scope, you could attach them as properties to the wrapping function, but it looks like you just need an object
var toggleVis = {
showSomething: function() {
trigger.classList.add("active");
subject.classList.add("active");
},
hideSomething: function() {
trigger.classList.remove("active");;
subject.classList.remove("active");
}
}
Your togleVis variable is a function and not an object so you can't do toggleVis.hideSomething(). Try updating your code to :
var toggleVis = (function() {
return {
showSomething : function () {
trigger.classList.add("active");
subject.classList.add("active");
},
hideSomething : function () {
trigger.classList.remove("active");;
subject.classList.remove("active");
}
};
}());
With this toggleVis is now an object with two properties showSomething and hideSomething functions.

Custom Events in CLASS

I need to launch custom events from CLASS. I know to do this with DOM objects and jquery, using triggerHandler, like $(object)..triggerHandler("inputChange", {param:X});
The problem is when i try this with a Class, like this:
var MyClass = (function(){
var static_var = 1;
var MyClass = function () {
var privateVar;
var privateFn = function(){ alert('Im private!'); };
this.someProperty = 5;
this.someFunction = function () {
alert('Im public!');
};
this.say = function() {
alert('Num ' + this.someProperty);
$(this).triggerHandler("eventCustom");
}
this.alter = function() {
this.someProperty ++;
}
};
return MyClass;
})();
TheClass = new MyClass();
$(TheClass).on('eventCustom', function() {
alert('Event!');
});
TheClass.say();
This doesn't launch warnings or errors, but the events listener is not working (or event is not dispatched). I think the jQuery event system doesn't work with not DOM object, correct?
Any other way (I need events, not callbacks for my specific case) to launch the events?
Thanks a lot!
I wrote an ES6 event class for nowadays in under 100 lines of code without using JQuery. If you don't want to use DOM-events you can extend your class, which should deal with Events.
For listening to events, you can use on, once, onReady, onceReady. On is execute the callbackfunction every time the label is trigger. Once only one time. The "ready"-functions execute the callback, if the label had been already triggerd before.
For triggering an event, use a trigger. To remove an eventhandler, use off.
I hope the example makes it clear:
class ClassEventsES6 {
constructor() {
this.listeners = new Map();
this.onceListeners = new Map();
this.triggerdLabels = new Map();
}
// help-function for onReady and onceReady
// the callbackfunction will execute,
// if the label has already been triggerd with the last called parameters
_fCheckPast(label, callback) {
if (this.triggerdLabels.has(label)) {
callback(this.triggerdLabels.get(label));
return true;
} else {
return false;
}
}
// execute the callback everytime the label is trigger
on(label, callback, checkPast = false) {
this.listeners.has(label) || this.listeners.set(label, []);
this.listeners.get(label).push(callback);
if (checkPast)
this._fCheckPast(label, callback);
}
// execute the callback everytime the label is trigger
// check if the label had been already called
// and if so excute the callback immediately
onReady(label, callback) {
this.on(label, callback, true);
}
// execute the callback onetime the label is trigger
once(label, callback, checkPast = false) {
this.onceListeners.has(label) || this.onceListeners.set(label, []);
if (!(checkPast && this._fCheckPast(label, callback))) {
// label wurde nocht nicht aufgerufen und
// der callback in _fCheckPast nicht ausgeführt
this.onceListeners.get(label).push(callback);
}
}
// execute the callback onetime the label is trigger
// or execute the callback if the label had been called already
onceReady(label, callback) {
this.once(label, callback, true);
}
// remove the callback for a label
off(label, callback = true) {
if (callback === true) {
// remove listeners for all callbackfunctions
this.listeners.delete(label);
this.onceListeners.delete(label);
} else {
// remove listeners only with match callbackfunctions
let _off = (inListener) => {
let listeners = inListener.get(label);
if (listeners) {
inListener.set(label, listeners.filter((value) => !(value === callback)));
}
};
_off(this.listeners);
_off(this.onceListeners);
}
}
// trigger the event with the label
trigger(label, ...args) {
let res = false;
this.triggerdLabels.set(label, ...args); // save all triggerd labels for onready and onceready
let _trigger = (inListener, label, ...args) => {
let listeners = inListener.get(label);
if (listeners && listeners.length) {
listeners.forEach((listener) => {
listener(...args);
});
res = true;
}
};
_trigger(this.onceListeners, label, ...args);
_trigger(this.listeners, label, ...args);
this.onceListeners.delete(label); // callback for once executed, so delete it.
return res;
}
}
// +++ here starts the example +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class TestClassEvents extends ClassEventsES6 {
constructor() {
super();
this.once('sayHallo', this.fStartToTalk);
this.on('sayHallo', this.fSayHallo);
}
fStartToTalk() {
console.log('I start to talk... ');
}
fSayHallo(name = 'Nobody') {
console.log('Hallo ' + name);
}
}
let testClassEvents = new TestClassEvents();
testClassEvents.trigger('sayHallo', 'Tony');
testClassEvents.trigger('sayHallo', 'Tim');
testClassEvents.onReady('sayHallo', e => console.log('I already said hello to ' + e));
testClassEvents.trigger('sayHallo', 'Angie');
testClassEvents.off('sayHallo');
testClassEvents.trigger('sayHallo', 'Peter');
console.log('I dont say hallo to Peter, because the event is off!')
Your understanding of how javascript works is limited since you are approaching it from a traditional OOP point of view. Take a look at this fiddle http://jsfiddle.net/9pCmh/ & you will see that you can actually pass functions as variables to other functions. There are no classes in javascript, only functions which can be closures which can be made to emulate traditional classes:
var MyClass = (function(){
var static_var = 1;
var MyClass = function ( callback ) {
var privateVar;
var privateFn = function(){ alert('Im private!'); };
this.someProperty = 5;
this.someFunction = function () {
alert('Im public!');
};
this.say = function() {
alert('Num ' + this.someProperty);
callback();
}
this.alter = function() {
this.someProperty ++;
}
};
return MyClass;
})();
TheClass = new MyClass(function() {
alert('Event!');
});
TheClass.say();
Alternatively you could create a function in your "class" to configure the callback/trigger instead of passing it into the constructor.
Have a look at this as a start for your further reading on this concept... How do JavaScript closures work?
Edit
To appease those critics looking for an eventQueue here is an updated jsfiddle :)
http://jsfiddle.net/Qxtnd/9/
var events = new function() {
var _triggers = {};
this.on = function(event,callback) {
if(!_triggers[event])
_triggers[event] = [];
_triggers[event].push( callback );
}
this.triggerHandler = function(event,params) {
if( _triggers[event] ) {
for( i in _triggers[event] )
_triggers[event][i](params);
}
}
};
var MyClass = (function(){
var MyClass = function () {
this.say = function() {
alert('Num ' + this.someProperty);
events.triggerHandler('eventCustom');
}
};
return MyClass;
})();
TheClass = new MyClass();
events.on('eventCustom', function() {
alert('Event!');
});
events.on('eventCustom', function() {
alert('Another Event!');
});
TheClass.say();

How to make a function reference with the 'this' keyword

I'm creating a small tooltip application and I'm having trouble. I'm trying to add an event to the document, but am having trouble referencing the function that needs to be executed. Here is the code:
var Note, note;
(function () {
'use strict';
// Helper functions
function addEvent(to, type, fn) {
if (to.addEventListener) {
to.addEventListener(type, fn, false);
} else if (to.attachEvent) {
to.attachEvent('on' + type, fn);
} else {
to['on' + type] = fn;
}
}
// Temporary constructor
function Temp() {
this.dragging = false;
return this;
}
Temp.prototype = {
listen: function () {
this.dragging = true;
},
drag: function () {
alert('hi 1');
if (!this.dragging) return;
alert('hi 2');
},
create: function () {
// unimportant code ...
addEvent(document, 'mousedown', this.drag);
// unimportant code ...
}
};
window.Note = Temp;
}());
note = new Note();
note.create(); // the note is created as planned
note.listen(); // alert(note.dragging) yields true
If there are small mistakes in the code I don't think those are the problem, the code on my system passes JSLint (I know that doesn't guarantee correctness). Neither of the alerts alert their arguments; I suspect, though, that the problem is assigning 'this.drag' as the function reference to the event handler. Are there any workarounds for this?
Thank you all for your time!
Try next:
(function () {
'use strict';
// Helper functions
function addEvent(to, type, fn) {
if (to.addEventListener) to.addEventListener(type, fn, false);
else if (to.attachEvent) to.attachEvent('on' + type, fn);
else to['on' + type] = fn; // this is bad. this do not support multiple handlers
}
// Temporary constructor
function Temp() {
this.dragging = false;
}
Temp.prototype = {
constructor: Temp, // needed because it is removed when used Temp.prototype={...}
listen: function () {
this.dragging = true;
},
drag: function () {
alert('hi 1');
if (!this.dragging) return;
alert('hi 2');
},
create: function () {
//addEvent(document, 'mousedown', this.drag);
addEvent(document, 'mousedown', this.drag.bind(this));
// or in older maner (when .bind() not implemented):
//var self=this;
//addEvent(document, 'mousedown', function(){self.drag();});
}
};
window.Note = Temp;
})();
var note = new Note();
note.create(); // the note is created as planned
note.listen(); // alert(note.dragging) yields true

Categories

Resources