Where does an anonymous function gets its arguments - javascript

I'm learning to develop Windows 8 style applications with the help of a book. The chapter I'm reading focuses on HTML, CSS and JavaScript languages for developing. The application displays in a ListView the images you have in the My Pictures Folder and deletes them when the user clicks or taps an image. Here is the code that implements the deletion of an image in the ListView:
var lv = document.getElementById('lv');
lv.addEventListener('iteminvoked', function (eventObj) {
eventObj.detail.itemPromise.then(function (listViewItem) {
var binding = files.dataSource.createListBinding();
binding.fromIndex(listViewItem.index).then(function (dataItem) {
var key = dataItem.key;
files.dataSource.remove(key);
binding.release();
});
});
});
My question is, where does the eventObj parameter of the anonymous function in the addEventListener method gets its value? I have found a similar question asked here: Passing arguments in anonymous functions in JavaScript, but i cannot fully understand it. I searched the documentation for addEventListener on MSDN but it just says it takes an event handler function, but it doesn't say anything about the parameters. Thanks in advance.

It's rather simple: whatever function internally calls that callback passes the arguments. See, addEventListener tells the executing Javascript engine to call the callback function you specify whenever an event occurs. The javascript engine saves your anonymous function in some variable - and cann call it later on using that exact variable, passing any number of arguments.
To illustrate it, consider something like this the internal function that handels events (purlely fictional, just to illustrate how it could be done):
var callbacks = [];
function addEventListener(newEvent, newCallback) {
callbacks.push({event : newEvent, callback : newCallback});
}
function handleEvent (someEvent) {
for (var i = 0 ; i < callbacks.length ; i++ ) {
if (callbacks[i].event == someEvent.name) {
callbacks[i].callback(someEvent);
}
}
}
Some more explanation:
As javascript is a so-called "functional language", functions are just values of variables.
function someFunc () {}
is actually just some kind of shortcut (technically it's not, but it does the same thing) for
var someFunc = function () {}
This having said, it's of cours possible to associate multiple names with one function:
var someFunc = function () {}
var sameFunc = someFunc;
var stillSame = somefunc;
var alsoSame = stillSame;
and you can call that function using any of those names, including passing arguments of course:
var someFunc = function (arg) { alert(arg); }
var sameFunc = someFunc;
sameFunc("It worx");
You can even call a function without ever naming it:
(function () {alert("test")})();<
or
(function (arg) { alert(arg); })("test")
Using this concept to perversion finally leads (long way to go however) to things like the y-combinator.

Event handlers may be attached to various objects including DOM
elements, document, the window object, etc. When an event occurs, an
event object is created and passed sequentially to the event
listeners.
Source: https://developer.mozilla.org/en-US/docs/Web/API/Event
An event listener or event handler can be an anonymous function or named function, it really doesn’t matter. The point is that it’s the event interface that defines the event object that is passed to the handler.
To find out exactly the event property from the event you are using, please refer to the windows docs: http://msdn.microsoft.com/en-us/library/windows/apps/br211827.aspx

The arguments recieved by the event listener are sent from the dispatchEvent, i.e. when the event dispatched it passes an event object to your handler.
Refer to this documentation on how to create and dispatch the event. The event object can vary in structure to convey information to the eventhandler to execute necessary steps. So in your case when you do lv.dispatchEvent(newevent) this sends an newevent as eventObj to your event handler.
Keep in mind there can be multiple eventhandlers listening to an event so the browser maintains a stack for the eventlisteners running them sequentially with each of them passed eventObj.
Anonymous function is no different from a named function. In JavaScript functions are first-class objects meaning regular objects. So you can pass them like regular objects(numbers,strings) without having to name them. Only thing is reuse becomes an issue.

What you need to understand this code is to rewrite it a bit:
var lv = document.getElementById('lv'),
invokeHandler = function (eventObj) {
var promiseFullfilled = function (listViewItem) {
var binding = files.dataSource.createListBinding(),
anotherPromiseFullfilled = function (dataItem) {
var key = dataItem.key;
files.dataSource.remove(key);
binding.release();
};
binding.fromIndex(listViewItem.index).then(anotherPromiseFullfilled);
};
eventObj.detail.itemPromise.then(promiseFullfilled);
};
lv.addEventListener('iteminvoked', invokeHandler);
This code works just the same, however it is now obvious that addEventListener or then actually do not know anything about the callback functions they are passed with. They can, however, use Function.prototype.call or Function.prototype.apply to apply arguments:
// This is PSEUDOCODE, event model actually works in a totally different way
HTMLElement.prototype.addEventListener = function(eventType, callback, bubbles) {
// callbacks is some internal collection for this specific element, probably available via a closure, looks something like:
// {
// 'someEventType': [callback1, callback2],
// 'someOtherEvent': [callback1, callback3, callback4]
// }
callbacks[eventType].push(callback);
}
// This is called whenever an event is triggered on an element
HTMLElement.prototype.dispatchEvent = function(event) {
callbacks[event.type].forEach( function(callback) {
return callback.call(this, event); // the callback is called with 'this' set to the element DOM object, and 'event' is the first argument
});
// then it can bubble or cancel depending on the event type and callback results
}

it is a CustomEvent, and all the process is like that:
//you add a anonymous function to a specific listener
lv.addEventListener('iteminvoked', function (eventObj) {
console.log(eventObj===myEvent);
});
//somewhere in your code a CustomEvent gets created based on "iteminvoked" key
var myEvent = new CustomEvent("iteminvoked", {
itemInfo: {
name: "yourItem"
},
bubbles: true,
cancelable: false
});
//somewhere when an item gets invoked this code raise the `iteminvoked` trigger
lv.dispatchEvent(myEvent);
all the functions that are passed as a listener are stored based on the key, something like:
var observers = {
"iteminvoked" : [f1, f2],
//other keys
}
it doesn't have anything to do with not having name, the function object is stored in the some kind of array. and dispatchEvent goes thru the array and invokes all the functions, and pass the myEvent as their parameter. It is a Observer pattern, implemented in javascript, I have implemented it once in my own javascript library like:
var lv = /*your element*/;
if(observers["iteminvoked"]){
for(var i=0;i<observables["iteminvoked"].length;i++){
var func = observables["iteminvoked"][i];
var o = func.call(lv, myEvent);
//this line is to support return false
if(o!==undefined && o===false) break;
}
}
as you can see it is dispatchEvent resplonsiblity to invoke all the observers, and your function no matter it has name or not gets invoked with the lv as the this context and myEvent as the parameter.

Related

Pass parameters to event listener that can be removed javascript

I searched everywhere here for an answer to satisfy this question, but I couldn't find it. I did find this answer for action script and others that were similar, but none directly answered my question for Javascript.
I have an event listener which needs to pass in a parameter like so:
var parameter = "";
myVar.addEventListener('event', function(e){
function_To_Call(e,parameter);
}, false);
How do I remove this event listener when I'm done?
Here is another solution that will not need to keep a var outside of the function scope holding your custom value. By using closures. You will still have an outside function handle, but it looks cleaner to me and less error prone.
const handlerFabric = (someArg) => {
return (e) => {
//Here you have access to your someArg, as well as the e coming in
console.log(e);
console.log(someArg);
}
}
const handler = handlerFabric('test'); //Set your custom arg here
myVar.addEventListener('event', handler);
myVar.EventListener('event', handler);
You need to have a standalone reference to the function you pass into addEventListener. For example:
var parameter = "";
const callback = function(e){
function_To_Call(e,parameter);
}
myVar.addEventListener('event', callback);
// ...
myVar.removeEventListener('event', callback);
Whether parameter changes in the meantime is of no consequence, because the callback function is still static.
Note that to reduce unnecessary code, feel free to omit the third parameter to addEventListener, useCapture, which defaults to false already.
Don't use an anonymous function as the listener. Do something like:
var parameter = "";
var listenerFunction = function(event, parameter) {
// code for function_To_Call goes here
}
myVar.addEventListener(event, listenerFunction);
And when you're done:
myVar.removeEventListener(event, listenerFunction);
See https://www.w3schools.com/jsref/met_element_removeeventlistener.asp

javascript passing event in closure

I was trying the following:
f.addEventListener('submit',(function(frm){
var func = (function(e){somefunction(e,frm);})(e);
})(f),false);
But this is failing. I want to pass the form (f) as a static reference and the dynamic event object to the named function 'somefunction'.
What I have above isnt working, what is the right syntax for passing both?
The issue is that each of the functions is being called right away, with undefined actually being passed to addEventListener().
You'll want to instead return one of the functions without its calling parenthesis so the event can call it later:
f.addEventListener('submit', (function (frm) {
return function (e) {
someFunction(e, frm);
};
})(f), false);
Though, with event bindings, you may not necessarily need the closure, as the <form> will be the context (this) of the function passed:
f.addEventListener('submit', someFunction, false);
function someFunction(e) {
var frm = this;
// ...
}
not saure exactly what you are trying to do but, to looks like you are trying to manually pass in the form via the event handler. Instead save a reference and just refer to it in the handler such as
f.addEventListener('submit',function(){
var func = function(e){
somefunction(e,f);
};
},false);
you shouldn't need the self executing functions unless I am missing your intent here

Associating unique properties to anonymous functions in JavaScript

What's a standard way of associating unique properties to anonymous JavaScript functions while creating them so that these properties can be accessed within the functions themselves when they are executed (i.e. run time)?
A scenario is this:
Say I have events which I want to bind to dynamically generated anonymous functions.
var events = ['connect','disconnect','error','connect_failed'];
for(a in events){
var handler = function(){
// console.log(arguments.callee.custom); // Not reliable 'cos callee is supposedly deprecated
console.log('<event-name>');
};
handler.custom = events[a];
$(window).on(events[a],handler);
}
Since using arguments.callee is deprecated and not guaranteed to be optimal on all platforms. What's the recommended way of achieving something similar to this?
You can use handler.custom in your function, too:
var handler = function() {
console.log(handler.custom);
console.log('<event-name>');
};
To prevent scoping issues in case of asynchronous callbacks, you can create a newly scoped handler function by wrapping your code in a closure:
for(a in events){
(function() {
var handler = function(){
console.log(handler.custom);
console.log('<event-name>');
};
handler.custom = events[a];
$(window).on(events[a],handler);
})();
}
EDIT: just realized you can use forEach as well (although that also suffers from browser compatibility issues):
events.forEach(function(event) {
var handler = function() {
...
};
handler.custom = event;
$(window).on(event,handler);
});
Named function expressions give a reference to the function itself only within the function scope:
var events = ['connect', 'disconnect', 'error', 'connect_failed'];
for (var a in events) {
var handler = function inside() {
console.log(inside.custom); // not deprecated
console.log('<event-name>');
};
handler.custom = events[a]; // now will stay with function even if renamed or moved
$(window).on(events[a], handler);
}
However, Internet Explorer 8 and below will not parse named functions correctly - Juriy Zaytsev explains the exact issue behind them in more detail: http://kangax.github.io/nfe/#named-expr
If you really have to target IE8 and below, either stick with arguments.callee or use conditional compilation for IE, which is basically IE conditional comments for JavaScript.
You could do something like this:
var events = ['connect','disconnect','error','connect_failed'];
function assignHandler(eventType, custom) {
$(window).on(events[a], function() {
console.log(custom);
});
}
for(a in events){
assignHandler(events[a], events[a]);
}
The magic of JS closures means that the anonymous function assigned as an event handler inside assignHandler() will be able to access the custom argument of assignHandler() even after assignHandler() has finished.
However, since you seem to be using jQuery's .on() method you don't need to implement anything yourself because jQuery already has functionality for this:
.on( events [, selector ] [, data ], handler(eventObject) )
Note the optional third argument is data. Within the event handler this can be accessed as event.data.
for(a in events){
var handler = function(e){
console.log(e.data);
console.log('<event-name>');
};
var custom = events[a];
$(window).on(events[a], null, custom, handler);
}
I know what you mean...
you want event name when the event is triggered and want to know which event has triggered at run time in function... Am I Right? then you can use Event Object received in handler...
DEMO
var events = ['connect','disconnect','error','connect_failed'];
for(a in events){
var handler = function(e){
console.log(e.type);
console.log('<event-name>');
};
$(window).on(events[a],handler);
}
for(a in events){
$(window).trigger(events[a]);
}

Bind and unbind a bound function to an event

I am using EventEmitter2 as message bus internal within my application. Now I need to bind and unbind some event handlers. As I want them also to bind them to a given context, I end up with the following syntax:
messageBus.on('foo::bar', _.bind(eventHandler, this));
The problem is that I need to unbind them at a later point in time, so I wrote:
messageBus.off('foo::bar', _.bind(eventHandler, this));
Unfortunately this does not work, as _.bind each time returns a new instance of the wrapper function. Now of course I could run _.bind once and bind the wrapped function, such as this:
var fn = _.bind(eventHandler, this);
messageBus.on('foo::bar', fn);
messageBus.off('foo::bar', fn);
This works perfectly well, but if you have a few event handlers, the code quickly starts to get less readable than necessary.
How might you solve this without the need to externalize the call to the bind function? Is there an alternative function that always returns the same wrapper in case you call it multiple times and the function as well as the context are the same?
Underscore.js provides a bindAll method just for this use-case, from the documentation:
Binds a number of methods on the object, specified by methodNames, to be run in the context of that object whenever they are invoked
Failing that, you could make use of a closure which always returns the same bound function when invoked, ie:
function getOrCreateBoundEventHandlerFor(eventType, callback) {
// Initialise the handler map if it's not already been created.
this._boundEventHandlerMap = this._boundEventHandlerMap || {};
// If no handler was mapped, create a new one.
if (this._boundEventHandlerMap[eventType] === void 0) {
this._boundEventHandlerMap[eventType] = _.bind(callback, this);
}
return this._boundEventHandlerMap[eventType];
}

Firefox add-on - `this` works in one method but fails in another method of the same object

I am developing an add-on for Firefox (3.6.*). in the following code notify called from inside init works fine, but I get an error saying this.notify is not a function when it is called from within onPageLoad. Why is that?
Also when I change the call to myextobj.notify('title', 'msg'), it works. The same is true for accessing variables. So, what is the difference between this and the object name as a prefix?
var myextobj = {
init: function() {
this.notify('init', 'We are inside init');
...
var appcontent = document.getElementById("appcontent"); // browser
if(appcontent)
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
},
onPageLoad: function(aEvent) {
this.notify('onPageLoad', 'We are inside onPageLoad');
...
},
notify: function (title, text) {
Components.classes['#mozilla.org/alerts-service;1'].
getService(Components.interfaces.nsIAlertsService).
showAlertNotification(null, title, text, false, '', null);
}
};
window.addEventListener("load", function() { myextobj.init(); }, false);
When you do this:
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
you just add the function that is hold in onPageLoad as event handler. The connection to the object is lost and this will refer to the global object when executed.
Just create an anonymous function as you do for the load event:
var that = this; // capture reference to object
appcontent.addEventListener("DOMContentLoaded", function(event) {
that.onPageLoad(event);
// myextobj.onPageLoad(event); should also work in this case
}, true);
Remember that functions are first class objects in JavaScript, they can be passed around like any other value. Functions have no reference to an object they are defined on, because they don't belong to that object. They are just another kind of data.
To which object this refers to in a function is decided upon execution and depends on the context the function is executed in. If you call obj.func() then the context is obj, but if you assign the function to another variable before like var a = obj.func (that is wat you do with adding the event handler (in a way)) and then call a(), this will refer to the global object (which is window most of the time).
When onPageLoad is called for the event, 'this' would not be referring to your myextobj. Because it wasn't called in the context of your object myextobj.
The way I deal with this is, by having all member functions of an object using the following convention.
var myObj = {
.....
counter: 0,
.....
myFunction: function () {
var t = myObj;
t.myOtherFunc();
},
....
myOtherFunc: function() {
var t = myObj;
t.counter++;
}
};
See how, I'm aliasing myObj as t, to save on typing and making my intent of using this clear.
Now you can call your methods safely from any context without worrying about what this would be referring to. Unless you really want the standard behavior; in that case, you may like to look at the call and apply methods. This link might help: Function.apply and Function.call in JavaScript
You may also want to look at a recent addition to JavaScript (would be available in FireFox 4): the bind method: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Another link, which directly addresses your problem: https://developer.mozilla.org/en/DOM/element.addEventListener#The_value_of_this_within_the_handler
The other way to add an event listener without losing track of this is to pass this itself as the event listener. However you are limited in that the function is always called handleEvent, so it's less useful if you have many listeners (unless they are all for different events, in which case you can switch on the event's type).

Categories

Resources