Removing DOM Event Observer - javascript

I'm working in a javascript based system with some legacy code (ominous music), and this legacy code adds event listeners like this
foo.addEventListener("click",function(){
//do some stuff
});
Is there a way for me to programmatically remove event listeners that have been added like this? I know about removeEventListener, but's it's not clear from the documentation how (if at all) to remove a listener which a programmer added via an anonymous function.

As far as I can tell, you can't use an anonymous function if you want to call removeEventListener because you need a reference to the same function that you used with addEventListener and the only way to do that is to have a name for the function (e.g. not anonymous).
Similar question and conclusion here: removeEventListener on anonymous functions in JavaScript
Without changing the structure of your code, you can give it a name and then use that name later.
foo.addEventListener("click", function fooClickHandler(){
//do some stuff
});
// then later
foo.removeEventListener("click", fooClickHandler);

You can remove them the same way that you add them by passing in the handler that you created it with. The handler ceases to be an anonymous function at this point though:
var handler = function() {
// do some stuff
};
foo.addEventListener("click", handler);
foo.removeEventListener("click", handler);
You can do some funky stuff like this to remove handlers with anonymous functions although I don't recommend it, but it is possible and you can't access callee in strict mode:
document.addEventListener('click', function(e) {
console.log('click');
if(e.unbind) {
document.removeEventListener('click', arguments.callee);
}
});
var event;
// First actual real event
event = new Event('click');
document.dispatchEvent(event);
// Unbinding event
event = new Event('click');
event.unbind = true;
document.dispatchEvent(event);
// Should not fire
event = new Event('click');
document.dispatchEvent(event);

If you want to get rid of all eventlisteners before adding your own you can use clonenode and remove original. The copy will not have the eventlisteners copied with it.
var fee = foo.cloneNode();
foo.parentNode.replaceChild(fee, foo);
Should look the same but without eventlistener.
Got a fiddle here that proves my point: http://jsfiddle.net/U7w7M/1/
Notice how the formatting stays, the position is the same, but click action is removed after first click

Related

removeEventlistener not working as expected with arrow function and parameter [duplicate]

This question already has an answer here:
Update or Change or Remove/Reset Javascript event listener
(1 answer)
Closed 6 years ago.
I've got a page which can hold multiple editable contents. I want to fire some kind of check event whenever the content is edited.
My code to achieve this looks like this:
// Find all editable elements.
let allEditableElements = document.querySelectorAll('[contenteditable="true"]');
for(let i = 0; i < allEditableElements.length; i++){
//Remove eventListener to prevent duplicate events.
allEditableElements[i].removeEventListener('input', (event) => {myClass.myEventMethod(event);}, false);
// Add event.
allEditableElements[i].addEventListener('input', (event) => {myClass.myEventMethod(event);}, false);
}
Everything works fine so far. But as I said users can edit the content, which includes adding new editable contents to the page itself. At that point the events will be set again, which is why I'm trying to remove the event beforehand.
My question is why would the removeEventListener function not work as expected? And isn't there a way to name given events like so:
// With eventNameGivenByUser an event could be removed just by its name.
addEventListener('eventTriggerName', 'eventNameGivenByUser', function(), [, options]);
Of course I did some research and found out that the code itself would work like this:
// Find all editable elements.
let allEditableElements = document.querySelectorAll('[contenteditable="true"]');
for(let i = 0; i < allEditableElements.length; i++){
//Remove eventListener to prevent duplicate events.
allEditableElements[i].removeEventListener('input', myClass.myEventMethod, false);
// Add event.
allEditableElements[i].addEventListener('input', myClass.myEventMethod, false);
}
However this is without passing parameters, which is mandatory in such a dynamic setup...
Hope someone will tell me that in 2017 there is a nice and decent way without using libraries.
Edit 08.02.2017:
Just for the curious ones:
The Solution is to not pass any parameter to the listener:
// as you can see there is no (event) and no arrow function either.
addEventListener('input', myClass.myEventMethod, false);
All there is to do now is to call prepare the method like this:
// The Parameter will be passed through anyway!
myEventMethod(event) {
/**
* Do stuff with event parameter.
*/
};
After that the listener can be removed like so:
removeEventListener('input', myClass.myEventMethod, false);
Sidenote:
I'm using electron and do not need cross browser support. It just has to be compatible with Chromium: 56.0.2924.87.
Regards, Megajin
The removeEventListener method takes at least two arguments: the event name and the listener function to remove.
In your first example:
allEditableElements[i].removeEventListener('input', (event) => {myClass.myEventMethod(event);}, false);
you are defining a new function which wasn't previously attached as event listener, so it can not be removed.
I don't know what's wrong with your second attempt:
allEditableElements[i].removeEventListener('input', myClass.myEventMethod, false);
this one should work fine. However you could combine both approaches: wrap your class method in a function, and attach the wrapped version as an listener. You just need to have a reference to make it possible to remove later:
const inputListener = (event) => { myClass.myEventMethod(event); };
let allEditableElements = document.querySelectorAll('[contenteditable="true"]');
for(let i = 0; i < allEditableElements.length; i++){
//Remove eventListener to prevent duplicate events.
allEditableElements[i].removeEventListener('input', inputListener, false);
// Add event.
allEditableElements[i].addEventListener('input', inputListener, false);
}
All that said I'd advise to just use event delegation. Attach the listener on an element higher up in the DOM and don't worry about clearing and re-adding event listeners when new elements appear:
const handleContentEditable = e => {
if( e.target.isContentEditable ){
console.log( 'editing ', e.target );
}
};
document.body.addEventListener('input', handleContentEditable );
https://jsfiddle.net/rd3y9gh9/1/
This is because the arrow function in removeEventListener is creating a new function reference. removeEventListener requires a reference to the original method passed to addEventListener.
You will have to preserve a reference to the event method in your case OR you can recreate the input elements on the fly, this will remove all event listeners.
https://developer.mozilla.org/en/docs/Web/API/Node/cloneNode
Clone Node does not copy event listeners so you could probably use that to clone the original element and replace it.

Removing event listener with anonymous listener [duplicate]

Is there anyway to remove an event listener added like this:
element.addEventListener(event, function(){/* do work here */}, false);
Without replacing the element?
There is no way to cleanly remove an event handler unless you stored a reference to the event handler at creation.
I will generally add these to the main object on that page, then you can iterate and cleanly dispose of them when done with that object.
You could remove the event listener like this:
element.addEventListener("click", function clicked() {
element.removeEventListener("click", clicked, false);
}, false);
Anonymous bound event listeners
The easiest way to remove all event listeners for an element is to assign its outerHTML to itself. What this does is send a string representation of the HTML through the HTML parser and assign the parsed HTML to the element. Because no JavaScript is passed, there will be no bound event listeners.
document.getElementById('demo').addEventListener('click', function(){
alert('Clickrd');
this.outerHTML = this.outerHTML;
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>
Anonymous delegated event listeners
The one caveat is delegated event listeners, or event listeners on a parent element that watch for every event matching a set of criteria on its children. The only way to get past that is to alter the element to not meet the criteria of the delegated event listener.
document.body.addEventListener('click', function(e){
if(e.target.id === 'demo') {
alert('Clickrd');
e.target.id = 'omed';
}
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>
Old Question, but here is a solution.
Strictly speaking you can’t remove an anonymous event listener unless you store a reference to the function. Since the goal of using an anonymous function is presumably not to create a new variable, you could instead store the reference in the element itself:
element.addEventListener('click',element.fn=function fn() {
// Event Code
}, false);
Later, when you want to remove it, you can do the following:
element.removeEventListener('click',element.fn, false);
Remember, the third parameter (false) must have the same value as for adding the Event Listener.
However, the question itself begs another: why?
There are two reasons to use .addEventListener() rather than the simpler .onsomething() method:
First, it allows multiple event listeners to be added. This becomes a problem when it comes to removing them selectively: you will probably end up naming them. If you want to remove them all, then #tidy-giant’s outerHTML solution is excellent.
Second, you do have the option of choosing to capture rather than bubble the event.
If neither reason is important, you may well decide to use the simpler onsomething method.
Yes you can remove an anonymous event listener:
const controller = new AbortController();
document.addEventListener(
"click",
() => {
// do function stuff
},
{ signal: controller.signal }
);
You then remove the event listener like this:
controller.abort();
You may try to overwrite element.addEventListener and do whatever you want.Something like:
var orig = element.addEventListener;
element.addEventListener = function (type, listener) {
if (/dontwant/.test(listener.toSource())) { // listener has something i dont want
// do nothing
} else {
orig.apply(this, Array.prototype.slice.apply(arguments));
}
};
ps.: it is not recommended, but it will do the trick (haven't tested it)
Assigning event handlers with literal functions is tricky- not only is there no way to remove them, without cloning the node and replacing it with the clone- you also can inadvertantly assign the same handler multiple times, which can't happen if you use a reference to a handler. Two functions are always treated as two different objects, even if they are character identical.
Edit: As Manngo suggested per comment, you should use .off() instead of .unbind() as .unbind() is deprecated as of jQuery 3.0 and superseded since jQuery 1.7.
Even though this an old question and it does not mention jQuery I will post my answer here as it is the first result for the searchterm 'jquery remove anonymous event handler'.
You could try removing it using the .off() function.
$('#button1').click(function() {
alert('This is a test');
});
$('#btnRemoveListener').click(function() {
$('#button1').off('click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button1">Click me</button>
<hr/>
<button id="btnRemoveListener">Remove listener</button>
However this only works if you've added the listener using jQuery - not .addEventListener
Found this here.
If you're using jQuery try off method
$("element").off("event");
Jquery .off() method removes event handlers that were attached with .on()
With ECMAScript2015 (ES2015, ES6) language specification, it is possible to do with this nameAndSelfBind function that magically turns an anonymous callback into a named one and even binds its body to itself, allowing the event listener to remove itself from within as well as it to be removed from an outer scope (JSFiddle):
(function()
{
// an optional constant to store references to all named and bound functions:
const arrayOfFormerlyAnonymousFunctions = [],
removeEventListenerAfterDelay = 3000; // an auxiliary variable for setTimeout
// this function both names argument function and makes it self-aware,
// binding it to itself; useful e.g. for event listeners which then will be able
// self-remove from within an anonymous functions they use as callbacks:
function nameAndSelfBind(functionToNameAndSelfBind,
name = 'namedAndBoundFunction', // optional
outerScopeReference) // optional
{
const functionAsObject = {
[name]()
{
return binder(...arguments);
}
},
namedAndBoundFunction = functionAsObject[name];
// if no arbitrary-naming functionality is required, then the constants above are
// not needed, and the following function should be just "var namedAndBoundFunction = ":
var binder = function()
{
return functionToNameAndSelfBind.bind(namedAndBoundFunction, ...arguments)();
}
// this optional functionality allows to assign the function to a outer scope variable
// if can not be done otherwise; useful for example for the ability to remove event
// listeners from the outer scope:
if (typeof outerScopeReference !== 'undefined')
{
if (outerScopeReference instanceof Array)
{
outerScopeReference.push(namedAndBoundFunction);
}
else
{
outerScopeReference = namedAndBoundFunction;
}
}
return namedAndBoundFunction;
}
// removeEventListener callback can not remove the listener if the callback is an anonymous
// function, but thanks to the nameAndSelfBind function it is now possible; this listener
// removes itself right after the first time being triggered:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
e.target.removeEventListener('visibilitychange', this, false);
console.log('\nEvent listener 1 triggered:', e, '\nthis: ', this,
'\n\nremoveEventListener 1 was called; if "this" value was correct, "'
+ e.type + '"" event will not listened to any more');
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to prove that deanonymized functions -- even when they have the same 'namedAndBoundFunction'
// name -- belong to different scopes and hence removing one does not mean removing another,
// a different event listener is added:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
console.log('\nEvent listener 2 triggered:', e, '\nthis: ', this);
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to check that arrayOfFormerlyAnonymousFunctions constant does keep a valid reference to
// formerly anonymous callback function of one of the event listeners, an attempt to remove
// it is made:
setTimeout(function(delay)
{
document.removeEventListener('visibilitychange',
arrayOfFormerlyAnonymousFunctions[arrayOfFormerlyAnonymousFunctions.length - 1],
false);
console.log('\nAfter ' + delay + 'ms, an event listener 2 was removed; if reference in '
+ 'arrayOfFormerlyAnonymousFunctions value was correct, the event will not '
+ 'be listened to any more', arrayOfFormerlyAnonymousFunctions);
}, removeEventListenerAfterDelay, removeEventListenerAfterDelay);
})();
//get Event
let obj = window; //for example
let eventStr= "blur"; //for example
let index= 0; //you can console.log(getEventListeners(obj)[eventStr]) and check index
let e = getEventListeners(obj)[eventStr][index];
//remove this event
obj .removeEventListener(eventStr,e.listener,e.useCapture);
THE END :)
i test in chrome 92, worked
How I used options parameter for my customEvent
options Optional
An object that specifies characteristics about the event listener. The available options are:
...
**once**
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
for my custom function that I created, it worked quite nicely.
const addItemOpenEventListener = (item, openItem) => {
document.addEventListener('order:open', ({detail}) => {
if(detail.id === item.id) {
openItem();
}
}, {once: true})
};
el.addItemOpenEventListener(item, () => dispatch(itemOpen)()));
checked my console, seems like it worked (any feedback appreciated!)
The following worked well enough for me. The code handles the case where another event triggers the listener's removal from the element. No need for function declarations beforehand.
myElem.addEventListener("click", myFunc = function() { /*do stuff*/ });
/*things happen*/
myElem.removeEventListener("click", myFunc);

Safely using hook events in JavaScript

In source code here
http://www.daftlogic.com/sandbox-javascript-slider-control.htm
There is these instructions:
// safely hook document/window events
if (document.onmousemove != f_sliderMouseMove) {
window.f_savedMouseMove = document.onmousemove;
document.onmousemove = f_sliderMouseMove;
}
I don't understand what it does and why it would be safer to do that this way, does someone understand?
It might be that some other code already assigned an event handler to document.onmousemove. The problem with this method, as opposed to addEventListener, is that only one function can be assigned to element.onXXXX. Thus, if you blindly assign a new event handler, an already existing one might be overwritten and other code might break.
In such a case, I would write:
if (document.onmousemove) {
(function() {
var old_handler = document.onmousemove;
document.onmousemove = function() {
old_handler.apply(this, arguments);
f_sliderMouseMove.apply(this, arguments);
};
}());
}
else {
document.onmousemove = f_sliderMouseMove;
}
This way it is ensured that both event handlers are executed. But I guess that depends on the context of the code. Maybe f_sliderMouseMove calls window.f_savedMouseMove anyway.
It is just saving the current hook, presumably so it can call it at the end of its own hook method.
It avoids stamping on some other codes hook that was already set up.
You would expect the hook code to be something like:
f_sliderMouseMove = function(e) {
// Do my thing
// Do their thing
window.f_savedMouseMove();
}
[obligatory jquery plug] use jquery events and you can ignore problems like this...
It appears that this code is storing the function that is currently executed on a mouse move, before setting the new one. That way, it can presumably be restored later, or delegated to, if need be. This should increase compatibility with other code or frameworks.

Remove EventListener in JavaScript after event occurred?

There are 24 div-objects waiting/listening for a mouse-click. After click on one div-object, I want to remove the EventListener from all 24 div-objects.
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener()//Problem lies here
}
//Some other code to be run after mouseclick
},false);
}
The problem is that the removeEventListener is nested in addEventListener and I need to define type, listener, caption as attributes to the removeEventListener method. And I think it is impossible to define the listener because of nesting.
I also tried to define a function name, but it didn't worked:
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function helpme(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener('click',helpme,false);
}
//Some other code to be run after mouseclick
},false);
}
You can tell the event listener to simply fire just once:
document.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
The documentation says:
once:
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
It should work with a named function. If your second approach does not work properly, try storing the initial listener into a variable, like this:
var handler = function(event) {
for(...) {
removeEventListener('click', handler, false);
}
};
addEventListener('click', handler, false);
Ps. if you care about speed, you may wish to consider using just one event handler. You can put the handler into the parent element of the divs, and then delegate the event from there. With 24 handlers your current approach probably doesn't have a very big performance hit, but this is something you should keep in mind if it ever feels slow.
For those who needs to remove after a certain condition (or even inside a loop too), one alternative is using AbortController and AbortSignal:
const abortController = new AbortController();
let handler = function(event) {
if(...) {
abortController.abort();
}
};
addEventListener('click', handler, {signal: abortController.signal});
The same answer:
element.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
You can read more here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

How do I add and remove an event listener using a function with parameters?

Sorry if this is a common question, but I couldn't find any answers that seemed pertinent through searching.
If I attach an event listener like this:
window.addEventListener('scroll', function() { check_pos(box); }, false);
it doesn't seem to work to try to remove it later, like this:
window.removeEventListener('scroll', function() { check_pos(box); }, false);
I assume this is because the addEventListener and removeEventListener methods want a reference to the same function, while I've provided them with anonymous functions, which, while identical in code, are not literally the same.
How can I change my code to get the call to removeEventListener to work? The "box" argument refers to the name of an <iframe> that I'm tracking on the screen; that is, I want to be able to subscribe to the scroll event once for each <iframe> that I have (the quantity varies), and once the check_pos() function measures a certain position, it will call another function and also remove the event listener to free up system resources.
My hunch is that the solution will involve a closure and/or naming the anonymous function, but I'm not sure exactly what that looks like, and would appreciate a concrete example.
Hope that makes sense.
Have you tried maintaining a reference to the anonymous function (like you suggested)?
So:
var listener = function() {
check_pos(box);
};
window.addEventListener('scroll', listener, false);
...
window.removeEventListener('scroll', listener, false);
Mozilla's docs suggest the same thing.
var listener;
listener = function(){
if( window.target != anotherEvent.target )
{
...CODE where
window.removeEventListener('click', listener , false);
};
window.addEventListener('click', listener ,false);
document.getElementById("yourId").removeEventListener("click",yourfunction1);
document.getElementById("yourId").addEventListener("click",yourfunction2);
function yourfunction1(){
//write code here
alert(1);
}
function yourfunction2(){
//write code here
alert(2);
}
<button type="button" onclick="yourfunction1()" id="yourId">Button</button>

Categories

Resources