Javascript unbind Anonymous callback Function [duplicate] - javascript

This question already has answers here:
How to removeEventListener that is addEventListener with anonymous function?
(5 answers)
Closed 10 years ago.
Is there any possibility to unbind anonymous callback function ...
link.addEventListener("click", function () {
//Any piece of code
}, true);
link.removeEventListener("click", function () {
//Any piece of code
});
Thanks,
Ajain

No. Because those to anonymouse functions are actually different. It is the same reason why { a: 1 } === { a: 1 } returns false.
You need to do the following:
var func = function () { ... };
element.addEventListener( 'click', func, false );
element.removeEventListener( 'click', func, false );

Yes. You can do this by saving a handle to the anonymous event handler function somewhere using arguments.callee and later using this saved reference to unbind the same.
// binding
var el = document.getElementById('foo');
el.addEventListener('click', function(e) {
alert('Hi');
// this is where we save a handle to the anonymous handler
// arguments.callee gives us a reference to this function
el['_ev'] = arguments.callee;
}, false);
// unbinding
var el = document.getElementById('foo'), handler = el['_ev'];
if(handler) {
// here we use the saved reference to unbind it
el.removeEventListener('click', handler);
el['_ev'] = false;
}
Demo: http://jsfiddle.net/jrXex/2/

Functions are identified by pointer. You have no pointer to your anonymous function, so you have nothing to pass to remove() to tell it which function to remove.
Simply passing a duplicate function doesn't do it, because the duplicate has a different pointer.
You need to stick with assigning the function to a variable, then passing that variable to remove().

Related

Pass arguments to functions in addEventListener [duplicate]

The situation is somewhat like-
var someVar = some_other_function();
someObj.addEventListener("click", function(){
some_function(someVar);
}, false);
The problem is that the value of someVar is not visible inside the listener function of the addEventListener, where it is probably being treated as a new variable.
Why not just get the arguments from the target attribute of the event?
Example:
const someInput = document.querySelector('button');
someInput.addEventListener('click', myFunc, false);
someInput.myParam = 'This is my parameter';
function myFunc(evt)
{
window.alert(evt.currentTarget.myParam);
}
<button class="input">Show parameter</button>
JavaScript is a prototype-oriented language, remember!
There is absolutely nothing wrong with the code you've written. Both some_function and someVar should be accessible, in case they were available in the context where anonymous
function() { some_function(someVar); }
was created.
Check if the alert gives you the value you've been looking for, be sure it will be accessible in the scope of anonymous function (unless you have more code that operates on the same someVar variable next to the call to addEventListener)
var someVar;
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click", function(){
some_function(someVar);
}, false);
This question is old but I thought I'd offer an alternative using ES5's .bind() - for posterity. :)
function some_func(otherFunc, ev) {
// magic happens
}
someObj.addEventListener("click", some_func.bind(null, some_other_func), false);
Just be aware that you need to set up your listener function with the first param as the argument you're passing into bind (your other function) and the second param is now the event (instead of the first, as it would have been).
Quite and old question but I had the same issue today. Cleanest solution I found is to use the concept of currying.
The code for that:
someObj.addEventListener('click', some_function(someVar));
var some_function = function(someVar) {
return function curried_func(e) {
// do something here
}
}
By naming the curried function it allows you to call Object.removeEventListener to unregister the eventListener at a later execution time.
You can just bind all necessary arguments with 'bind':
root.addEventListener('click', myPrettyHandler.bind(null, event, arg1, ... ));
In this way you'll always get the event, arg1, and other stuff passed to myPrettyHandler.
http://passy.svbtle.com/partial-application-in-javascript-using-bind
nice one line alternative
element.addEventListener('dragstart',(evt) => onDragStart(param1, param2, param3, evt));
function onDragStart(param1, param2, param3, evt) {
//some action...
}
You can add and remove eventlisteners with arguments by declaring a function as a variable.
myaudio.addEventListener('ended',funcName=function(){newSrc(myaudio)},false);
newSrc is the method with myaudio as parameter
funcName is the function name variable
You can remove the listener with
myaudio.removeEventListener('ended',func,false);
Function.prototype.bind() is the way to bind a target function to a particular scope and optionally define the this object within the target function.
someObj.addEventListener("click", some_function.bind(this), false);
Or to capture some of the lexical scope, for example in a loop:
someObj.addEventListener("click", some_function.bind(this, arg1, arg2), false);
Finally, if the this parameter is not needed within the target function:
someObj.addEventListener("click", some_function.bind(null, arg1, arg2), false);
You could pass somevar by value(not by reference) via a javascript feature known as closure:
var someVar='origin';
func = function(v){
console.log(v);
}
document.addEventListener('click',function(someVar){
return function(){func(someVar)}
}(someVar));
someVar='changed'
Or you could write a common wrap function such as wrapEventCallback:
function wrapEventCallback(callback){
var args = Array.prototype.slice.call(arguments, 1);
return function(e){
callback.apply(this, args)
}
}
var someVar='origin';
func = function(v){
console.log(v);
}
document.addEventListener('click',wrapEventCallback(func,someVar))
someVar='changed'
Here wrapEventCallback(func,var1,var2) is like:
func.bind(null, var1,var2)
Here's yet another way (This one works inside for loops):
var someVar = some_other_function();
someObj.addEventListener("click",
function(theVar){
return function(){some_function(theVar)};
}(someVar),
false);
someVar value should be accessible only in some_function() context, not from listener's.
If you like to have it within listener, you must do something like:
someObj.addEventListener("click",
function(){
var newVar = someVar;
some_function(someVar);
},
false);
and use newVar instead.
The other way is to return someVar value from some_function() for using it further in listener (as a new local var):
var someVar = some_function(someVar);
one easy way to execute that may be this
window.addEventListener('click', (e) => functionHandler(e, ...args));
Works for me.
Use
el.addEventListener('click',
function(){
// this will give you the id value
alert(this.id);
},
false);
And if you want to pass any custom value into this anonymous function then the easiest way to do it is
// this will dynamically create property a property
// you can create anything like el.<your variable>
el.myvalue = "hello world";
el.addEventListener('click',
function(){
//this will show you the myvalue
alert(el.myvalue);
// this will give you the id value
alert(this.id);
},
false);
Works perfectly in my project. Hope this will help
If I'm not mistaken using calling the function with bind actually creates a new function that is returned by the bind method. This will cause you problems later or if you would like to remove the event listener, as it's basically like an anonymous function:
// Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', myCallback);
someObject.removeEventListener('event', myCallback);
// Not Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', function() { myCallback });
someObject.removeEventListener('event', /* can't remove anonymous function */);
So take that in mind.
If you are using ES6 you could do the same as suggested but a bit cleaner:
someObject.addEventListener('event', () => myCallback(params));
$form.addEventListener('submit', save.bind(null, data, keyword, $name.value, myStemComment));
function save(data, keyword, name, comment, event) {
This is how I got event passed properly.
One way is doing this with an outer function:
elem.addEventListener('click', (function(numCopy) {
return function() {
alert(numCopy)
};
})(num));
This method of wrapping an anonymous function in parentheses and calling it right away is called an IIFE (Immediately-Invoked Function Expression)
You can check an example with two parameters in http://codepen.io/froucher/pen/BoWwgz.
catimg.addEventListener('click', (function(c, i){
return function() {
c.meows++;
i.textContent = c.name + '\'s meows are: ' + c.meows;
}
})(cat, catmeows));
In 2019, lots of api changes, the best answer no longer works, without fix bug.
share some working code.
Inspired by all above answer.
button_element = document.getElementById('your-button')
button_element.setAttribute('your-parameter-name',your-parameter-value);
button_element.addEventListener('click', your_function);
function your_function(event)
{
//when click print the parameter value
console.log(event.currentTarget.attributes.your-parameter-name.value;)
}
Sending arguments to an eventListener's callback function requires creating an isolated function and passing arguments to that isolated function.
Here's a nice little helper function you can use. Based on "hello world's" example above.)
One thing that is also needed is to maintain a reference to the function so we can remove the listener cleanly.
// Lambda closure chaos.
//
// Send an anonymous function to the listener, but execute it immediately.
// This will cause the arguments are captured, which is useful when running
// within loops.
//
// The anonymous function returns a closure, that will be executed when
// the event triggers. And since the arguments were captured, any vars
// that were sent in will be unique to the function.
function addListenerWithArgs(elem, evt, func, vars){
var f = function(ff, vv){
return (function (){
ff(vv);
});
}(func, vars);
elem.addEventListener(evt, f);
return f;
}
// Usage:
function doSomething(withThis){
console.log("withThis", withThis);
}
// Capture the function so we can remove it later.
var storeFunc = addListenerWithArgs(someElem, "click", doSomething, "foo");
// To remove the listener, use the normal routine:
someElem.removeEventListener("click", storeFunc);
There is a special variable inside all functions: arguments. You can pass your parameters as anonymous parameters and access them (by order) through the arguments variable.
Example:
var someVar = some_other_function();
someObj.addEventListener("click", function(someVar){
some_function(arguments[0]);
}, false);
I was stuck in this as I was using it in a loop for finding elements and adding listner to it. If you're using it in a loop, then this will work perfectly
for (var i = 0; i < states_array.length; i++) {
var link = document.getElementById('apply_'+states_array[i].state_id);
link.my_id = i;
link.addEventListener('click', function(e) {
alert(e.target.my_id);
some_function(states_array[e.target.my_id].css_url);
});
}
I suggest you to do something like that:
var someVar = some_other_function();
someObj.addEventListener("click", (event, param1 = someVar) => {
some_function(param1);
}, false);
The PERFECT SOLUTION for this is to use Closures like this:
function makeSizer(size) {
return function () {
document.body.style.fontSize = `${size}px`;
};
}
//pass parameters here and keep the reference in variables:
const size12 = makeSizer(12);
const size24 = makeSizer(24);
const size36 = makeSizer(36);
document.getElementById('size-12').addEventListener("click", size12);
document.getElementById('size-24').addEventListener("click", size24);
document.getElementById('size-36').addEventListener("click", size36);
document.getElementById('remove-12').addEventListener("click", ()=>{
document.getElementById('size-12').removeEventListener("click", size12);
alert("Now click on 'size 12' button and you will see that there is no event listener any more");
});
test<br/>
<button id="size-12">
size 12
</button>
<button id="size-24">
size 24
</button>
<button id="size-36">
size 36
</button>
<button id="remove-12">
remove 12
</button>
So basically you wrap a function inside another function and assign that to a variable that you can register as an event listener, but also unregister as well!
Also try these (IE8 + Chrome. I dont know for FF):
function addEvent(obj, type, fn) {
eval('obj.on'+type+'=fn');
}
function removeEvent(obj, type) {
eval('obj.on'+type+'=null');
}
// Use :
function someFunction (someArg) {alert(someArg);}
var object=document.getElementById('somObject_id') ;
var someArg="Hi there !";
var func=function(){someFunction (someArg)};
// mouseover is inactive
addEvent (object, 'mouseover', func);
// mouseover is now active
addEvent (object, 'mouseover');
// mouseover is inactive
Hope there is no typos :-)
The following answer is correct but the below code is not working in IE8 if suppose you compressed the js file using yuicompressor. (In fact,still most of the US peoples using IE8)
var someVar;
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click",
function(){
some_function(someVar);
},
false);
So, we can fix the above issue as follows and it works fine in all browsers
var someVar, eventListnerFunc;
someVar = some_other_function();
eventListnerFunc = some_function(someVar);
someObj.addEventListener("click", eventListnerFunc, false);
Hope, it would be useful for some one who is compressing the js file in production environment.
Good Luck!!
var EV = {
ev: '',
fn: '',
elem: '',
add: function () {
this.elem.addEventListener(this.ev, this.fn, false);
}
};
function cons() {
console.log('some what');
}
EV.ev = 'click';
EV.fn = cons;
EV.elem = document.getElementById('body');
EV.add();
//If you want to add one more listener for load event then simply add this two lines of code:
EV.ev = 'load';
EV.add();
The following approach worked well for me. Modified from here.
function callback(theVar) {
return function() {
theVar();
}
}
function some_other_function() {
document.body.innerHTML += "made it.";
}
var someVar = some_other_function;
document.getElementById('button').addEventListener('click', callback(someVar));
<!DOCTYPE html>
<html>
<body>
<button type="button" id="button">Click Me!</button>
</body>
</html>
Since your event listener is 'click', you can:
someObj.setAttribute("onclick", "function(parameter)");
Another workaround is by Using data attributes
function func(){
console.log(this.dataset.someVar);
div.removeEventListener("click", func);
}
var div = document.getElementById("some-div");
div.setAttribute("data-some-var", "hello");
div.addEventListener("click", func);
jsfiddle
The following code worked fine for me (firefox):
for (var i=0; i<3; i++) {
element = new ... // create your element
element.counter = i;
element.addEventListener('click', function(e){
console.log(this.counter);
... // another code with this element
}, false);
}
Output:
0
1
2
You need:
newElem.addEventListener('click', {
handleEvent: function (event) {
clickImg(parameter);
}
});

Javascript: Bind/unbind function behavior

When I bind a function with the parent this passed in thisArg, I can't unbind the same named function expression, but without it I can unbind the function itself. Why is that?
This works:
choicesList.addEventListener("click", function() {
const self= this;
document.addEventListener("click", function checkClick(e) {
if (!e) e = event;
if (!self.contains(e.target)) {
document.removeEventListener("click", checkClick);
}
}, false);
});
This doesn't:
choicesList.addEventListener("click", function() {
document.addEventListener("click", function checkClick(e) {
if (!e) e = event;
if (!this.contains(e.target)) {
document.removeEventListener("click", checkClick);
}
}.bind(this), false);
});
The reason for this is issue is that calling bind() on a function returns a new instance of that function:
function someHandler() {
alert('hi');
}
const someHandlerBinded = someHandler.bind(document);
// Returns false, seeing as these are different instances of the function
console.log( someHandlerBinded === someHandler );
By setting an event handler directly, via the result of bind() as you are in your second block of code, this causes a new instance of that function handler to be passed to addEventListener(). This in turn means that the subsequent attempt to removing this handler on line:
document.removeEventListener("click", checkClick);
will fail, seeing that the the defined function checkClick is not the same as the actual handler function used for that click event (ie the new function instance returned from function checkClick(){ ... }.bind())
One way to resolve this might be the following:
choicesList.addEventListener("click", function() {
// Declare the bound version of the click handler
const boundClickHandler = function checkClick(e) {
if (!e) e = event;
if (!this.contains(e.target)) {
// Removing the result of bind, rather than the declared
// checkClick handler
document.removeEventListener("click", boundClickHandler);
}
}.bind(this)
// Adding the result of bind as you currently are doing
document.addEventListener("click", boundClickHandler, false);
});
It's because this is in a function that is nested within another function and the nested function doesn't have the same invocation context as the outer one. The first one works because you are caching the object that the outermost this is referencing and you are then able to correctly reference it in the inner function.
You can read more about the volatility of this here.

Javascript - closure in anonymous function referencing a global function using arguments - how do i use preventDefault?

In the following code, where would I use preventDefault? I have been testing it to make it work but have not found a good way to cancel the default submit action. Which function needs to be capture the event object?
Here is the code:
f_name = 'someFunc' ;
f_args(1,2,3) ;
if(element.addEventListener) {
element.addEventListener('submit',(function(name,args,element) {
return function() {
window[name].apply(element,args) ;
}
})(f_name,f_args,element)
,false) ; }
f_name = 'someFunc';
f_args = [1, 2, 3];
if (element.addEventListener) {
element.addEventListener('submit', (function (name, args, element) {
return function (e) {
// here
e.preventDefault();
window[name].apply(element, args);
};
})(f_name, f_args, element), false);
}
Assuming you want to preventDefault on only some event handlers, you would use preventDefault() inside the callback that you use to handle a specific event. In your example, this would the global function represented by the name argument which would be the someFunc global function.
If you wanted to do it for all event handlers (which doesn't make a lot of sense to me), then you'd do it in your code right before or after you call window[name].
Your handler function will be called with the event as a parameter
return function(evt) {
evt.preventDefault();
window[name].apply(element,args) ;
}

Passing Parameters into a Callback Function

I have a function that listens for a click on the screen and fires a callback. It is part of a Helper object (which is why is preceded by the term Helper in my sample code. That is irrelevant however.
var Helper = {
bodyClickListener: function(fn) {
var window = document.getElementsByTagName('body')[0];
window.click();
CORE.dom.on(window, 'click', function(event) {
CORE.dom.off(window, 'click');
fn(event);
});
}
}
I need to be able to pass a function into this function with a parameter that has been previously set.
function someFunction() {
var popup = document.getElementById('tagResultsPopup');
Helper.bodyClickListener(function(popup) {
return function(event) {
event.stopPropagation();
removePopup(popup);
};
}(document.getElementById('tagResultsPopup')));
function removePopup(element) {
if(element) {
element.parentNode.removeChild(element);
}
};
}
The code above works, but you'll notice that I have to set the popup variable inside of the callback function. It has already been set above. How do I pass a reference to the earlier variable into the callback function.
If I understand your question correctly, you don't need to do much. You can just use the popup variable defined outside.
var popup = document.getElementById('tagResultsPopup');
Helper.bodyClickListener(function(event) {
event.stopPropagation();
//Don't set it
//var popup = document.getElementById('tagResultsPopup');
removePopup(popup);//popup will refer to the correct variable
});
The function that you are passing to bodyClickListener is a closure. You can simply reference 'popup' inside that function without any problem. You don't have to create a new variable.
The answer was to use closure in this way:
Helper.bodyClickListener(function(popup) {
return function(event) {
event.stopPropagation();
removePopup(popup);
};
}(document.getElementById('tagResultsPopup')));
That way the callback function has access to the variable I pass into the parameter function. So here, the return is actually the function I am passing as the callback.

Pass multiple arguments along with an event object to an event handler

How can one pass multiple arguments along with an event object to an event handler without using Function.prototype.bind?
The event handler has a closure in it.
The below basic code won't work,
element.addEventListener("click", eventHandler(eventObj + arguments), false);
function eventHandler(eventObj + arguments) {
return function(){}; //a closure
}
I don't know how to pass event object and other arguments at the same time to an event handler.
Updated:
I have even tried using anonymous function inside addEventListener. Doing so, it seems that the control never reached to the closure inside the callback function.
element.addEventListener("click", function(e){
var data = {'event':e, 'args':arguments};
eventHandler(data);
}, false);
function eventHandler(data) {
return function(){ //this function is never executed
console.log(JSON.stringify(data));
};
}
So if I understand correctly, you want to add an event listener to the element, and configure some additional data that exists at the time you're adding the listener to be passed to the listener when it's called. If that's what you want to do, you just need a proper closure. Something like this, assuming you want to store the additional data in an object:
var extra_data = {one: "One", two: "Two"};
var make_handler = function (extra_data) {
return function (event) {
// event and extra_data will be available here
};
};
element.addEventListener("click", make_handler(extra_data));
I suspect you can't, but there is a trick:
element.clickArguments=new Object();
element.clickArguments.argument1=...;
element.clickArguments.argument2=...;
Now in your event handler reference the event emitting object.
helper function:
function curryRight( original ) {
var args = [].slice.call( arguments, 1 );
return function() {
return original.apply( this, [].slice.call( arguments ).concat( args ) );
};
}
Usage:
element.addEventListener("click", curryRight( eventHandler, 1, 2, 3 ), false );
Example:
function eventHandler( event, a, b, c ) {
console.log( event, a, b, c );
//Logs MouseEvent, 1, 2, 3
}
document.addEventListener("click", curryRight( eventHandler, 1, 2, 3 ), false );
I know this thread is old, but it's a common mistake I see here.
I have even tried using anonymous function inside addEventListener. Doing so, it seems that the control never reached to the closure inside the callback function.
--
#asur
You need to invoke the returned closure from eventHandler().
element.addEventListener("click", function(e){
var data = {'event':e, 'args':arguments};
eventHandler(data)(); // invoking the closure here
}, false);
If you won't you just get a closure returned to the binary nirvana.
Besides that it's preferred to #jmm's solution while it's much more clean.
I have a function that has a parameter for capturing the current relative URL, as well as other values. For the sake of simplicity, I will only mention the parameter for the URL.
Using "event" as a keyword, as here, captures the event object:
function Analyze(url) {
if (event) {
// do something with the event object, such as:
alert(event.timeStamp);
// ...
}
The function is invoked in a button with
onclick="Analyze(document.URL);".
Yes, I know that this form of invocation makes me a naughty boy, but there was a good excuse for it in this case.
This technique does not work if "event" is replaced with "e" or "x", but there may be other possibilities.

Categories