javascript event listener with param - javascript

I have a simple event listener:
function listen(evnt, elem, func) {
if (elem.addEventListener) // W3C DOM
elem.addEventListener(evnt,func,false);
else if (elem.attachEvent) { // IE DOM
var r = elem.attachEvent("on"+evnt, func);
return r;
}
return false;
}
I want to set listeners with a parameter. (the parameter is not set by the event, it is part of specific listener.
What I do and seems to work is:
function setlistener (param){
listen ('custom event', document,
function (e){
run_func_with_param(param);
}
);
}
But I dont understand if its correct because param is not supposed to be defined when the event is fired.
My question is - is it the right way to have run_func_with_param called, each time with the params that was set for it in setlistener? In other words, is param remembered and will be set to the right values when run_func_with_param will be called as result of an event? (there will be multiple listeners with different params for the same event).
Notes: No jQuery/other libraries please.
I'm using a custom event in this case.

When you use an anonymous function, the arguments and local variables from the parent scope are still available in the anonymous function.
Thus, your argument named param is available inside your anonymous function that is passed to listen(). It isn't passed to that function - it's just available directly from the parent scope.
Here's your function with some annotation in comments:
function setlistener (param){
// param is available here as an argument to setlistener
// as a normal function argument
listen ('custom event', document, function (e) {
// param is still available here directly from the parent scope
// in this anonymous function.
// This is one advantage of using anonymous functions.
if (e.data.back_button==false){
run_func_with_param(param);
}
});
}

Related

onclick assigned function with parameters

I'm not sure if this has been asked before because I don't know what it's called.
But why wouldn't a method like this work? Below is just a general example
<script>
document.getElementById('main_div').onclick=clickie(argument1,argument2);
function clickie(parameter1,parameter2){
//code here
}
</script>
The code above would work fine if the event handler was assigned without parameters, but with parameters, it doesn't work. I think I read online that to overcome this problem, you could use closures. I'm assuming it's because of the parentheses ( ) that is calling the function immediately instead of assigning it to the event?
Because you're calling the function immediately and returning the result, not referencing it.
When adding the parenthesis you call the function and pass the result back to onclick
document.getElementById('main_div').onclick = clickie(); // returns undefined
so it's actually equal to writing
document.getElementById('main_div').onclick = undefined;
which is not what you want, you want
document.getElementById('main_div').onclick = clickie;
but then you can't pass arguments, so to do that you could use an anonymous function as well
document.getElementById('main_div').onclick = function() {
clickie(argument1,argument2);
}
or use bind
document.getElementById('main_div').onclick = yourFunc.bind(this, [argument1, argument2]);
It is however generally better to use addEventListener to attach event listeners, but the same principle applies, it's either (without arguments)
document.getElementById('main_div').addEventListener('click', clickie, false);
or bind or the anonymous function to pass arguments etc.
document.getElementById('main_div').addEventListener('click', function() {
clickie(argument1,argument2);
}, false);
The easiest way is:
yourElement.onclick = yourFunc.bind(this, [arg1, arg2]);
function yourFunc (args, event) {
// here you can work with you array of the arguments 'args'
}
When you say onClick = function() {...} you are registering your function with some internal JavaScript library. So when the "click" happens, that library invokes your function.
Now imagine you're the author of that library and someone registered their function with it. How would you know how many parameters to pass to the function? How would you know know what kind of parameters to pass in?
clickie(argument1,argument2)
This means to invoke the function and return its return value.
clickie
This simply is a reference to the function (doesn't invoke/execute it)
To bind an event to a element, you need to use either the attachEvent or addEventListener method. For example.
/* Non IE*/
document.getElementById('main_div').addEventListener('click', function () {}, false);
/* IE */
document.getElementById('main_div').attachEvent('onclick', function () {});
A function name followed by parentheses is interpreted as a function call or the start of a function declaration. The a onclick property needs to be set to a function object. A function declaration is a statement, and is not itself a function. It doesn't return a reference to the function. Instead it has the side effect of creating a variable in the global scope that refers to a new function object.
function clickie(param) { return true; }
creates a global variable named clickie that refers to a function object. One could then assign that object as an event handler like so: element.onclick = clickie;. An anonymous function declaration (often confused with a closure; for the difference see Closure vs Anonymous function (difference?)) does return a function object and can be assigned to a property as an event handler, as follows:
element.onclick = function(event) { return true; };
But this doesn't work:
element.onclick = function clickie(event) { return true;};
Why? Because function clickie(event) { return true;} is a statement, not a function. It doesn't return anything. So there is nothing to be assigned to the onclick property. Hope this helps.

How to pass arguments in a function reference

I'm looking for a way to save reference to two this objects in one function called after an event triggers in jQuery - this reference to the object the method is defined in (so I can use this.anotherObjectFunction()) and this reference to the object that triggered the event - so that I can use $(this).someJQueryFunction later on. The way I'd like to do it is by passing a this (function object) reference as an argument to the function. Unfortunately, the function is to be called by jQuery, not me, so it's passed as a reference, i.e.
someFunction: function()
{
...
cell.$el.on('click', 'li.multiselect-option', this.myClickFunction);
...
},
myClickFunction: function(objectReference)
{
//It should be able to call methods of that object.
objectReference.anotherFunction();
//And reference to the clicked item.
$(this).html("Don't click me anymore!!!");
}
I'm aware of the fact that I can do something like
cell.$el.on('click', 'li.multiselect-option', myFunction.bind(this));
...
myClickFunction: function(event)
{
this.anotherFunction();
$(event.currentTarget).html("Don't click me anymore!!!");
}
But this workaround doesn't really answer the question as it doesn't show how to pass additional arguments and in the future there may be a necessity to pass another (no, I don't want to register them as fields in the object).
No anonymous functions are allowed unless they can be easily removed with cell.$el.off() function that will remove them and only them (there are some other function associated with the same objects and events at the same time and they should remain intact).
UPDATE:
By no anonymous functions I mean solutions like:
var self = this;
cell.$el.on('click', 'li.multiselect-option', function() {
self.MyClickFunction(self, this);
});
They will not work because I'll have to use cell.$el.off() with the function reference (3-argument prototype) to remove this single function and only it, leaving other functions bound to both the same element and event.
Jquery .on event has option to pass the argument as parameter in event handler like this
cell.$el.on('click', 'li.multiselect-option', {arg1:'arg1' , arg2:'arg2'} , myFunction);
...
myClickFunction: function(event)
{
alert(event.data.arg1);
alert(event.data.arg2);
this.anotherFunction();
$(event.currentTarget).html("Don't click me anymore!!!");
}
Passing data to the handler
If a data argument is provided to .on() and is not null or undefined,
it is passed to the handler in the event.data property each time an
event is triggered. The data argument can be any type, but if a string
is used the selector must either be provided or explicitly passed as
null so that the data is not mistaken for a selector. Best practice is
to use a plain object so that multiple values can be passed as
properties.
or another way
cell.$el.on('click', 'li.multiselect-option', function() {
myClickFunction("hai" , "bye");
});
myClickFunction: function(arg1, arg2)
{
alert(arg1);
alert(arg2);
}
And I would also suggest a plugin-free solution to the question "how to supply a parameter to function reference"
Example
function x(a){
console.log(a);
}
setInterval('x(1)',1000);

Where does an anonymous function gets its arguments

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.

Callbacks, anonymous functions and context

What is the difference between this code:
$('.percentage_field').change(function() {
update_percentage();
});
$('.inspect1').change(function(){
show_hide_targets();
});
And this code:
$('.percentage_field').change(
update_percentage
);
$('.inspect1').change(
show_hide_targets
);
When a callback runs in response to an event, this inside the function is set to the DOM element that is the target of the event.
In your first example, the anonymous function gets the this of the target element, but that this is not forwarded to the inner function call. Instead, the inner function runs with a this according to how it is invoked. (Here, it's a direct "raw" call (i.e., not called as a member function), so it runs with a this equal to window, in non-script mode.)
In your second example, the functions update_percentage and show_hide_targets get the this of the target element directly.
Consider an example:
function sayThis() { alert(this); }
someElem.addEventListener("click", function() { sayThis() });
someElem.addEventListener("click", sayThis);
someElem.addEventListener("click", function() { sayThis.call(this) });
The first will alert window (or undefined in strict mode); the second will alert the element the listener fired on. The third listener uses an anonymous function, but it invokes the inner function using .call(this), which forwards the original this to the inner function.

How to decide if a parameter is the click event

I have a function which can be called by either a click event or directly. When I call it directly, I need pass it a data parameter, so I've defined the function to accept a parameter to account for the direct call with the parameter.
function myFunc(param){
}
Within the function, I need to differentiate whether the function was called directly or from the click event, so what I thought I could simply check if param is set. If it's set, then the function is called directly. If it's not set, then it's called from the click event.
The problem is that a click event by default passes an event object. So even when the function is called by the click event, param won't be null.
So is there a way to check whether the param passed is the click event?
You don't need to duplicate any code. The proper way to do this is to handle the click event with an event handler, and the action you want to take in another function. For example:
// define main action
var doSomethingUseful = function(is_clicked) {
// write some code here
}
// define click handler
var myBtnClicked = function(e) {
doSomethingUseful(true);
}
// bind click event
$('#my_button').click(myBtnClicked);
Although, as was already mentioned to you, the fact that you need to know that information is usually a warning sign that there might be a simpler way to do things.
edit for example:
// define main action
var doSomethingUseful = function() {
// write some code here that doesn't care "why" it's being called
}
// define click handler
var myBtnClicked = function(e) {
// do the click-only stuff
// call the generic function
doSomethingUseful();
}
// bind click event
$('#my_button').click(myBtnClicked);
IE's before version 9 don't always pass the event argument, so the parameter will be be undefined if it is called from an IE click.
function myFunc(param){
if(!param) || param.clientX)// not called with a data param
}
You would do better to check for the data than for the event.
Check for the .clientX property that is passed to the click event.
function(param){
if(param.clientX){//this is a click event}
else
{//this is a call event};
};
You may want to use this one.
function(param){
if(param.target){//this is a click event}
else
{//this is a call event};
};
function myFunc(param) {
// which (aka keyCode) is a property set to params
// by jQuery for any event
// Check for object make you free to pass parameter
// as object also with string, array and so on
if (Object.prototype.toString(param) == '[object Object]' && param.which) {
// called by clck
console.log(param.which);
} else {
// called by function
console.log(param);
}
}
$('#clickable').click(myFunc)
// passing parameter as object
myFunc({
a: 'abc',
b: 'def'
});​
// passing parameter as string
myFunc('Hello');
// passing parameter as array
myFunc([1, 2, 2]);
and so on.
DEMO
jQuery(document).ready(function(){
$('#someID').click(function(){
myFunc(true);
});
myFunc();
});
function myFunc(param){
param ? alert("click"):alert("not click");
}

Categories

Resources