Access event object in event handler - javascript

When I try to attach event handler functions with parameters like :
myhandle.onclick = myfunction(param1,param2);
function myfunction(param1,param2){
}
Now I want to access the event object in my handler function. There is an approach mentioned on the web of sending event object, like:
myhandle.onclick = myfunction(event,param1,param2);
But its giving event object undefined when I test it out.
I know libraries make this stuff easy but I am looking for a native JS option.

myhandle.onclick = myfunction(param1,param2);
This is a common beginner error at a JavaScript level, and not something that libraries can really fix for you.
You are not assigning myfunction as a click handler, you are calling myfunction, with param1 and param2 as arguments, and assigning the return value of the function to onclick. myfunction doesn't return anything, so you'd be assigning undefined to onclick, which would have no effect..
What you mean is to assign a reference to myfunction itself:
myhandle.onclick= myfunction;
To pass the function some extra arguments you have to make a closure containing their values, which is typically done with an anonymous inline function. It can take care of passing the event on too if you need (though either way you need a backup plan for IE where the event object isn't passed as an argument):
myhandle.onclick= function(event) {
myfunction(param1, param2, event);
};
In ECMAScript Fifth Edition, which will be the future version of JavaScript, you can write this even more easily:
myhandle.onclick= myfunction.bind(window, param1, param2);
(with window being a dummy value for this which you won't need in this case.)
However, since many of today's browsers do not support Fifth Edition, if you want to use this method you have to provide an implementation for older browsers. Some libraries do include one already; here is another standalone one.
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner,
args.length===0? arguments : arguments.length===0? args :
args.concat(Array.prototype.slice.call(arguments, 0))
);
};
};
}

I'm not entirely sure what you're trying to do, but perhaps something like this is what you're looking for?
function myfunction(param1, param2){
return function(event) {
// For compatibility with IE.
if (!event) {
event = window.event;
}
alert("event = " + event + ", param1 = " + param1 + ", param2 = " + param2);
};
}
myhandle.onclick = myfunction(param1, param2);
The end result of this is that the anonymous function inside of myfunction will be called as the onclick handler, resulting in the event plus the two parameters being printed. The two parameters are fixed at the time that the event handler is added while event will change each time the user clicks on myhandle.

Try this:
if (!event) { event = window.event; }
inside of myfunction().

Related

Is there a way to pass a functions reference but also give it a parameter?

I am trying to generalize a single function for handling different events in JavaScript. Meaning that depending on the event listener that was called my function needs to behave in a different way. However, I can only pass the reference to my function, which is where my problem arises.
In order for my function to behave properly I need to know which event handler called it. But if I can only pass by reference, I can't pass a parameter which tells me who the function caller was. So is there a way to pass a functions reference but also allow it to accept a parameter?
Here is some general code that hopefully illustrates my question.
btn1.AddEventListener("Click", myFunction);
btn2.AddEventListener("Click", myFunction);
function myFunction(caller){
if(caller == someObject)
{
//do something
}
else
{
//do something
}
}
Inside the function, you can either examine the this, which will refer to the element the listener was added to, or the first parameter (the event)'s currentTarget, which will refer to the same thing:
function myFunction(event) {
if (event.currentTarget === btn1) {
// btn1 was clicked
} else {
// btn2 was clicked
}
}
Closure is useful for this kind of event handlers. To create a closure wrap the function call with another anonymous function like:
btn1.AddEventListener("Click", () => myFunction(btn1));
btn2.AddEventListener("Click", () => myFunction(btn2));
Also, you can pass additional arguments as much as you want like:
btn1.AddEventListener("Click", () => myFunction(btn1, another_param));
function myFunction(btn, param){
// ... do something with param.
}
With the traditional notation, this is equivalent to following:
btn1.AddEventListener("Click", function () {
return myFunction(btn1);
});

javascript implicit function variables

I have seen several examples of java script functions with parameters passed which are not located in the script but are implicitly passed in. For example:
function myFunction(xml) {
var xmlDoc = xml.responseXML;
document.getElementById("demo").innerHTML =
xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue;
}
Where is the "xml" defined or listed? Where can i find a listing of other implicit parameters?
I've also seen a function with the following:
$("body").click(function (event) {
// Do body action
var target = $(event.target);
if (target.is($("#myDiv"))) {
// Do div action
}
});
Where is "event" coming from or listed?
Thanks in advance.
These variables are called (function) parameters. This is a common feature of most programming languages. They are defined with a function, and simply serve as variables that are defined within the function. They do not need to be defined outside of the function previously, because they exist only for the function.
I believe you're confused because they're not declared with var (as they shouldn't be) because you're calling them "implicit variables." However, they are not implicit; they are defined with the function.
You can find the parameters to a function by looking at the documentation for the function, if you are using a library like jQuery. For example, the .click() function handler is defined like:
(If you can't see the image, it shows .click(handler), where handler is of Type: Function(Event eventObject))
As you can see, it defines the function parameter eventObject which you can "pass" in when you invoke a function. You can use any valid variable name to do so.
You can see this MDN documentation for more information on parameters.
Where is the "xml" defined or listed? Where can i find a listing of other implicit parameters?
Is listed in the very function definition. When I define a function like:
function greet( name , greeting ){
console.log('hi ' + name );
console.log(greeting);
}
name and greeting vars are just defined within the parenthesis in the function definition. You can just call that function passing literals or variables:
greeting('peter' , 'have a nice day');
//or:
var name = 'Francisco';
var greeting = 'qué pasa hombre';
greet(name , greeting);
In the second example, name and greeting vars happen to be called exactly like the internal function parameters. That is just by case, could be too:
var theAame = 'Francisco';
var theGreeting = 'qué pasa hombre';
greet(theName , theGreeting);
And would work exactly the same. Also, in javaScript, you can just pass more parameters to a function than the parameters actually defined in the function, and access them with the arguments keyword or the ES6 spread syntax.
This is javaScript bread and butter and any search on how does arguments and parameters work in javaScript will be useful to you. However, your second question is more tricky.
You're also asking about this kind of code:
$("body").click(function (event) {
// Do body action
var target = $(event.target);
if (target.is($("#myDiv"))) {
// Do div action
}
});
This is similar, but also is more complex. Here $("body").click() is a function that takes a parameter. This parameter happens to be another function. This is a feature not supported in some languages, but is pretty straightforward in javascript. You could also wrote that code this way:
function reactToClick (event) {
// Do body action
var target = $(event.target);
if (target.is($("#myDiv"))) {
// Do div action
}
}
$("body").click( reactToClick );
But, who is then calling that reactToClick function with that event parameter? Well, in this case, the browser does.
Any browser has some API's to register to events -like clicks- with function callbacks, and $.click() is just some syntactic helper over that mechanism. Since is the browser who is ultimately calling the function, is difficult to fully understand the internals -and I must admit I don't-.
However, you can set up your own non-browser-api-dependant javaScript code that invoke callbacks, and the parameters set up and function invocations works the same way:
function theCallback( name , options ){
console.log('Im a callback function fired by someCallbackRegister whenever its fire methods is called');
console.log('my callbackRegister name is: ' + name);
console.log('and the options provided in this call are: ' + options);
}
function someCallbackRegister( callback , registerName ){
return {
fire : function(options){
callback(registerName , options );
}
}
}
var listener = someCallbackRegister( theCallback , 'Johhny');
listener.fire({ foo : 'bar'});
In this example, is the listener who is invoking theCallback after it's fire method call, and setting up all the parameters to that theCallback function properly, just like the browser manages to pass an event object to the callback function you pass to $.click().
Hope this helps :-)
PS: This video about the javaScript event loop helped me a lot to understand how the browser api's work.
function myFunction(xml) {
}
Whoever invokes this myFunction will pass the details which will be saved to variable xml. It's JS language syntax - you don't need to define the type of variable here unlike Java.
Similarly, when you do this:
$("body").click(function (event) {
});
JS internally registers a callback method whenever the body is clicked. It internally passes the event details to the function. You can do console.log(event) and see what all details are listed there

Javascript: Stop onClick default

I get the feeling I am missing something obvious but just can see it.
A trigger on the click event is set:
anchor.addEventListener("click", myClickEvent );
function myClickEvent() {
this.preventDefault(); // fails
return false;
}
However the default onClick method still happens.
Can I cancel default propogation at the same time as I addEventListener or is there some other way of doing this. I cannot use .preventDefault() from within myClickEvent because the event is not available at that point.
Edit following comments for clarity:
The default onClick of the tag happens even though myClickEvent returns false.
element.addEventListener("click", myClickEvent );
function myClickEvent(event) {
event.preventDefault();
return false;
}
Returning false from a handler that was registered with addEventListener does nothing. See https://stackoverflow.com/a/1357151/227299 and https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault
The recommended way is to call event.preventDefault()
function myClickEvent(e) {
e.preventDefault()
}
Handlers from HTML attributes still work, but you should not use it since it's not as clear what return false means when compared to event.preventDefault() (not allow the link to be followed) and event.stopPropagation() (don't let the event bubble up)
There is no such function in the global scope called preventDefault -- you need to use the event that has been passed into your callback.
Also please make sure that you are attaching the event to something that can be clicked on (a form, anchor, button etc) that can potentially have an event prevented.
If you attach the event to a child/parent element nothing will work as you expected.
Nearly there thanks to Teemu's code but not quite.
There is a difference depending on how the function is called. Seemingly there is both a hidden parameter sent defining the value of 'this' and when called without parameters an automatic event object sent as the first visible parameter:
Call it without parameters:
anchor.addEventListener("click", myClickEvent );
function myClickEvent(param1, param2) {
// this = events parent element (<a>)
// this.href = <a href=...>
// param1 = event object
// param2 = undefined
oEvent = param1;
oEvent.preventDefault();
}
but then call it with parameters:
anchor.addEventListener("click", function (oEvent) { myClickEvent(oEvent, myParameter); });
function myClickEvent(param1, param2) {
// this = [object Window]
// this.href = undefined
// param1 = event object
// param2 = myParameter
oEvent = param1;
oEvent.preventDefault();
}
Notice how when parameters are sent manually the value of 'this' changes from the events parent element to '[object Window]', causing this.href to go wrong.
However the preventDefault now appears to be working reliably.

Passing 'this' and argument to addEventListener function without using bind

Subsequent to removeEventListener in bootstrapped addon not working when addon disabled, I am exploring other possibilities.
Beside using bind() and caching the bound function, is there a way to use 'this' and pass argument?
// works fine but can't pass argeement
contextMenu.addEventListener('popupshowing',
this.contextPopupShowing, false);
// passes the argument but 'this' is no longer available
contextMenu.addEventListener('popupshowing',
function(){this.contextPopupShowing(window);}, false);
I have been using a number of event listeners with bind() and I am looking for alternative methods without using bind()
I even tried to grab window with a recursive function from <menupopup id="contentAreaContextMenu" ...>
Update: bind() interferes with removeEventListener
Since we're talking restartless add-ons... A lot of restartless add-ons use unload and unloadWindow helper functions, to make it easier to implement shutdown properly and also help with stuff like addEventListener, so bear with me for a bit.
The helpers - unload
First, unload is a helper function that you pass another function to, that will be run upon shutdown (or can be called manually). Most implementations are extremely similar to this:
var unloaders = []; // Keeps track of unloader functions.
function unload(fn) {
if (typeof(fn) != "function") {
throw new Error("unloader is not a function");
}
unloaders.push(fn);
return function() {
try {
 fn();
}
catch (ex) {
Cu.reportError("unloader threw " + fn.toSource());
Cu.reportError(ex);
}
unloaders = unloaders.filter(function(c) { return c != fn; });
};
}
You'd then hook up shutdown to do the right thing:
function shutdown() {
...
for (let i = unloaders.length - 1; i >= 0; --i) {
try {
unloaders[i]();
}
catch (ex) {
Cu.reportError("unloader threw on shutdown " + fn.toSource());
Cu.reportError(ex);
}
}
unloaders.length = 0;
}
Using unload
Now you can do stuff like:
function startup() {
setupSomething();
unload(removeSomething);
setupSomethingElse();
var manualRemove = unload(removeSomethingElse);
...
if (condition) {
manualRemove();
  }
}
The helpers - unloadWindow
You'll usually want to create a second function unloadWindow to unload stuff when either your add-on is shut down or the window gets closed, whatever happens first. Not removing stuff when the window gets closed can be very tricky, and create Zombie compartments of your bootstrap.js and/or code modules very easily (this is from experience writing and reviewing restartless add-ons).
function unloadWindow(window, fn) {
let handler = unload(function() {
window.removeEventListener('unload', handler, false);
try {
fn();
}
catch (ex) {
Cu.reportError("window unloader threw " + fn.toSource());
Cu.reportError(ex);
}
});
window.addEventListener('unload', handler, false);
};
(Some people might want to "optimize" this, as to have only one "unload" handler, but usually you only have so unloadWindow calls that it won't matter.)
Putting it all together
Now you can .bind stuff and do whatever and let the the unloader closures keep track of it. Also, you can use this to keep your shut down code next to your initialization code, which might increase readability.
function setupWindow(window, document) {
var bound = this.contextPopupShowing.bind(this);
contextMenu.addEventListener('popupshowing', bound, false);
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
});
// Or stuff like
var element = document.createElement(...);
contextMenu.appendChild(element);
unloadWindow(window, function() {
contextMenu.removeChild(element);
});
// Or just combine the above into a single unloader
unloadWindow(window, function() {
contextMenu.removeEventListener('popupshowing', bound, false);
contextMenu.removeChild(element);
});
}
Before bind() was supported you had to save a reference to this outside the function. Then pass in a function that can forward the invocation the way you want.
var self = this;
contextMenu.addEventListener('popupshowing', function() {
self.contextPopupShowing.apply(self, arguments);
}, false);
In this case we use apply to set the context to self, our saved version of this, and to send it whatever arguments were passed to the anonymous function via the magic arguments keyword that contains the list of arguments that a function was passed when invoked.
You don't have to use bind for addEventListener. You can use handleEvent. It was in that topic I linked you too:
Removing event listener which was added with bind
MDN :: EventTarget.addEventListener - The value of "this" within the handler
handleEvent is actually a common way the javascript code in the firefox codebase does it.
Copied straight from MDN:
var Something = function(element) {
this.name = 'Something Good';
this.handleEvent = function(event) {
console.log(this.name); // 'Something Good', as this is the Something object
switch(event.type) {
case 'click':
// some code here...
break;
case 'dblclick':
// some code here...
break;
}
};
// Note that the listeners in this case are this, not this.handleEvent
element.addEventListener('click', this, false);
element.addEventListener('dblclick', this, false);
// You can properly remove the listners
element.removeEventListener('click', this, false);
element.removeEventListener('dblclick', this, false);
}
Where I mostly use bind is when doing a for loop and I make anonymous functions with something in the array like arr[i]. If I don't bind it then it always just takes the last element of the array, I have no idea why, and I hate it, so then I go to using [].forEach.call(arr, function(arrI).
http://2ality.com/2013/06/auto-binding.html
var listener = myWidget.handleClick.bind(myWidget);
domElement.addEventListener('click', listener);
...
domElement.removeEventListener(listener);

Getting function arguments within another function

I've heard that it's possible to do something like this
this[func].apply(this, Array.prototype.slice.call(arguments, 1));
But to have access to the arguments object I need to be inside that function.
So, if I am, for example, running a code in the function function1, is there a way to get the arguments of a different function function2?
The whole problem is that I want to add events to any given element. But this element might already have another event attached to it. so, if I have, for example, an 'onclick' event attached to an element, I would do the following:
var event = 'onclick';
if(typeof currentElement[event] === 'function'){
cf = currentElement[event];
f = function(){
cf();
func();
}
}else f = func;
currentElement[event] = f;
Now both functions, the new function and the previous function, are being called. The problem is that the arguments being passed to the previous function were lost with this method.
Does anyone know if it is possible to not lose the arguments when we call a function dynamically like in my example?
OBS: jQuery is not an option :/
Sounds like using addEventListener would work better for you. It lets you attach multiple listeners (functions) to a single element:
elem.addEventListener("click", function() {
console.log("click listener 1");
});
elem.addEventListener("click", function() {
console.log("click listener 2");
});
Note, according to MDN, addEventListener is supported in IE >= 9.
If you did want to continue down your current path, you could do:
f = function(){
cf.apply(this, arguments);
func.apply(this, arguments);
}
For you specific case, it's not necessary to figure out what arguments are passed to the function since you know what it is - the event object in all browsers except (older) IE where its nothing (undefined).
So the code can simply be:
var event = 'onclick';
if(typeof currentElement[event] === 'function'){
cf = currentElement[event];
f = function(e){
cf(e);
func(e);
}
} else f = func;
currentElement[event] = f;
If possible use addEventListner and attachEvent as fallback to play nicely with other scripts that may run on your page.
Additional discussion:
In general, it is never necessary to figure out what has been passed to the callback function because that doesn't even make sense. The programmer cannot determine what to pass to the callback, it's the event emitter that determines what gets passed. In this case its the event object but it's a general rule.
Say for example we have an API that queries our database and returns some data:
function my_api (query) {}; // returns an object where a callback may
// be attached to the oncomplete property
The API documentation mentions:
my_api() returns an API object.
API object - has one property - oncomplete where a callback may be attached to handle data returned from the api call. The callback will be called with one argument - the returned data or no argument (undefined) if an error occured.
OK. So we use it like this:
var db = my_api('get something');
db.oncomplete = function (data) {alert(data)};
Now, if we want to wrap that oncomplete event in another event handler, we don't need to know what data it accepts since it's the API object that determines what to pass to the function, not the function! So we simply:
var tmp = db.oncomplete;
db.oncomplete = function (x) {
new_callback(x);
tmp(x);
}
We get the x argument not from querying the previous callback but from the documentation.
somefunction.arguments //Use the arguments property to get arguments of another function

Categories