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.
Related
I have no idea of how to do this, I want to pass an argument to the callback function "ctrlDeleteItem":
document.querySelector('.container').addEventListener('click', ctrlDeleteItem);
But if I do that, I lose access to the event property, example:
document.querySelector('.container').addEventListener('click', ctrlDeleteItem(item));
function ctrlDeleteItem(item, event) {
return function() {
console.log(item);
console.log(event);
}
}
How can I still pass the event as a parameter? I can't find how to do it, thanks.
The accepted answer is incorrect because it will cause the ctrlDeleteItem function to be executed immediately. That can be shown by adding a console.log("test") statement to the ctrlDeleteItem() function - you'll get the message immediately.
The correct solution is to set up a "wrapper" callback function that will receive the event as usual and then have that wrapper call the actual callback function with whatever arguments are needed. The wrapper can pass the event to the actual callback if desired.
const item = 'foo';
document.querySelector('.container').addEventListener('click', function(evt){
ctrlDeleteItem(evt, item);
});
function ctrlDeleteItem(evt, item) {
console.log(evt.type, item);
console.log(event.target.className);
}
<div class="container">container</div>
Put event as an argument to the returned function in ctrlDeleteItem, so that it will be used properly as the event in the listener:
function ctrlDeleteItem(item) {
return function(event) {
console.log(item);
console.log(event);
}
}
const item = 'foo';
document.querySelector('.container').addEventListener('click', ctrlDeleteItem(item));
function ctrlDeleteItem(item) {
return function(event) {
console.log(item);
console.log(event.target.className);
}
}
<div class="container">container</div>
I need to have a bound event listener function to reference itself, but I don't see a way to access itself in strict mode (arguments.callee is not available).
See the following code snippet for an example:
function callback(boundParam, event) {
// This does not work here as not 'callback' was added
// to the event bus but 'boundCallback'
eventBus.removeListener(callback);
}
function foo () {
const boundCallback = callback.bind({}, 7);
eventButs.addListener(boundCallback);
}
What are my options?
This is NOT a duplicate of JavaScript: remove event listener as I need to reference a bound function!
Maybe inline the handler so that you dont need to bind:
function foo () {
function callback (){
//...
eventBus.removeListener(callback);
}
eventBus.addListener(callback);
}
You could create a function that returns a reference to the bound function when called. Let's call that function getBoundCallback. Then, append it to the argument list of the bind() call in order to receive the function in callback. You should then be able to call getBoundCallback and get back the actual bound function:
function callback(boundParam, getBoundCallback, event) {
eventBus.removeListener(getBoundCallback());
}
function foo() {
let boundCallback;
const getBoundCallback = () => boundCallback;
boundCallback = callback.bind({}, 7, getBoundCallback);
eventButs.addListener(boundCallback);
}
Note how the declaration and initialisation of boundCallback are separated because of the reference to getBoundCallback being required for the statement initialising boundCallback.
I have the following javascript-function:
onMouseMoved = (function(_this) {
console.log(_this)
return function(event) {
console.log(event)
return;
};
})(this);
How come the mousemove-event trigers
If I add this listener:
document.addEventListener('mousemove', onMouseMoved, false);
console.log(event) get's triggered but not console.log(_this)
but if I add this listener:
document.addEventListener('mouseenter', onMouseMoved, false);
It's the other way around, why?
You're not invoking console.log(this) in the event handler, you're invoking that immediately. It's an immediately-invoked function expression, or IIFE. It executes and returns a function which is assigned to onMouseMoved.
onMouseMoved = (function(_this) {
console.log(_this)
return function(event) {
console.log(event)
return;
};
})(this);
// console.log(_this) has already bee called at this point
// onMouseMoved has been set to the returned function:
//
// function(event) {
// console.log(event)
// return;
// };
document.addEventListener('mousemove', onMouseMoved, false);
document.addEventListener('mousemove', onMouseMoved, false);
That console.log(_this) is only going to be called when onMouseMoved is instantiated the first time.
That onMouseMove is defined as a function which is called immediately with a value of this (probably for function scoping reasons). That console logging of _this, isn't inside its event handling function, so it wouldn't be called on the events.
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().
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) ;
}