removeEventListener without knowing the function - javascript

Some of the third party plugin will attach the eventListener into the site. How to I remove the eventListener without knowing the function that attached.
I refer this removeEventListener but I can't get any clue to remove this.
Eg: getEventListeners(window) shows the events attached. But, when I try to remove the event using window.removeEventListener("eventname") is not working without knowing that function.
Please help, Thanks in advance.

getEventListeners(window) will return a map of events and their registered event listeners.
So for DOMContentLoaded event for example you can have many event listeners. If you know the index of the listener you want to remove (or if there exists only one), you can do:
var eventlistener = getEventListeners(window)["DOMContentLoaded"][index];
window.removeEventListener("DOMContentLoaded",
eventlistener.listener,
eventlistener.useCapture);

Unfortunately, you cannot do that. You need to have a reference to the event handler function in order to remove it by removeEventListener.
Your only option if you cannot get that reference would be by entirely replacing that Node.

Update: 2023
EventListeners can be removed without knowing the actual function reference. But it will only work in modern browsers.
Use AbortController to remove the event. With AbortSignal, you can simply get the signal to remove it for you:
Sample Code:
const controller = new AbortController();
const { signal } = controller;
window.addEventListener('resize', () => doSomething(), { signal });
controller.abort(); // It wll remove the "resize" event handler.
You can check and add a polyfill for older browsers

Related

Is there a way to check if a message event listener already exists?

Is there a way to check if a message event listener already exists?
I want to do something like:
if(noMessageEventListenerExists) {
globalThis.addEventListener('message', async function (data) {})
}
I saw that working for onClick events but then you have to specify the id of the Element you want to check.
Is there a way to do that for 'message' event listeners?
You need to save a reference to the event listener while creating, like this:
if(!globalThis.onmessage) {
globalThis.onmessage = globalThis.addEventListener('message', async function (data) {})
}
Otherwise its (probably) lost and you can't find the event listener anymore. Some browsers seem to have (had) a getEventListeners() function, but this seems like nothing that you should rely on.
See more here: removeEventListener without knowing the function

How to list all bound events [duplicate]

I have already looked at these questions:
How to find event listeners on a DOM node when debugging or from the JavaScript code?
can I programmatically examine and modify Javascript event handlers on html elements?
How to debug JavaScript/jQuery event bindings with Firebug (or similar tool)
however none of them answers how to get a list of event listeners attached to a node using addEventListener, without modifying the addEventListener prototype before the event listeners are created.
VisualEvent doesn't display all event listener (iphone specific ones) and I want to do this (somewhat) programmatically.
Chrome DevTools, Safari Inspector and Firebug support getEventListeners(node).
You can't.
The only way to get a list of all event listeners attached to a node is to intercept the listener attachment call.
DOM4 addEventListener
Says
Append an event listener to the associated list of event listeners with type set to type, listener set to listener, and capture set to capture, unless there already is an event listener in that list with the same type, listener, and capture.
Meaning that an event listener is added to the "list of event listeners". That's all. There is no notion of what this list should be nor how you should access it.
Since there is no native way to do this ,Here is the less intrusive solution i found (dont add any 'old' prototype methods):
var ListenerTracker=new function(){
var targets=[];
// listener tracking datas
var _elements_ =[];
var _listeners_ =[];
this.init=function(){
this.listen(Element,window);
};
this.listen=function(){
for(var i=0;i<arguments.length;i++){
if(targets.indexOf(arguments[i])===-1){
targets.push(arguments[i]);//avoid duplicate call
intercep_events_listeners(arguments[i]);
}
}
};
// register individual element an returns its corresponding listeners
var register_element=function(element){
if(_elements_.indexOf(element)==-1){
// NB : split by useCapture to make listener easier to find when removing
var elt_listeners=[{/*useCapture=false*/},{/*useCapture=true*/}];
_elements_.push(element);
_listeners_.push(elt_listeners);
}
return _listeners_[_elements_.indexOf(element)];
};
var intercep_events_listeners = function(target){
var _target=target;
if(target.prototype)_target=target.prototype;
if(_target.getEventListeners)return;
if(typeof(_target.addEventListener)!=='function'||typeof(_target.removeEventListener)!=='function'){
console.log('target=',target);
throw('\nListenerTracker Error:\nUnwrappable target.');
}
// backup overrided methods
var _super_={
"addEventListener" : _target.addEventListener,
"removeEventListener" : _target.removeEventListener
};
_target["addEventListener"]=function(type, listener, useCapture){
var listeners=register_element(this);
// add event before to avoid registering if an error is thrown
_super_["addEventListener"].apply(this,arguments);
// adapt to 'elt_listeners' index
var uc=(typeof(useCapture)==='object'?useCapture.useCapture:useCapture)?1:0;
if(!listeners[uc][type])listeners[uc][type]=[];
listeners[uc][type].push({cb:listener,args:arguments});
};
_target["removeEventListener"]=function(type, listener, useCapture){
var listeners=register_element(this);
// add event before to avoid registering if an error is thrown
_super_["removeEventListener"].apply(this,arguments);
// adapt to 'elt_listeners' index
useCapture=(typeof(useCapture)==='object'?useCapture.useCapture:useCapture)?1:0;
if(!listeners[useCapture][type])return;
var lid = listeners[useCapture][type].findIndex(obj=>obj.cb===listener);
if(lid>-1)listeners[useCapture][type].splice(lid,1);
};
_target["getEventListeners"]=function(type){
var listeners=register_element(this);
// convert to listener datas list
var result=[];
for(var useCapture=0,list;list=listeners[useCapture];useCapture++){
if(typeof(type)=="string"){// filtered by type
if(list[type]){
for(var id in list[type]){
result.push({
"type":type,
"listener":list[type][id].cb,
"args":list[type][id].args,
"useCapture":!!useCapture
});
}
}
}else{// all
for(var _type in list){
for(var id in list[_type]){
result.push({
"type":_type,
"listener":list[_type][id].cb,
"args":list[_type][id].args,
"useCapture":!!useCapture
});
}
}
}
}
return result;
};
};
}();
ListenerTracker.init();
EDIT
Suggestion from #mplungjan: modified to listen to wrappable targets (singleton|constructor). 'init' tracks Element and window .
exemple with other wrappable target:
ListenerTracker.listen(XMLHttpRequest);
Suggestion from #kodfire : You may get optionals arguments with the args property.
I can't find a way to do this with code, but in stock Firefox 64, events are listed next to each HTML entity in the Developer Tools Inspector as noted on MDN's Examine Event Listeners page and as demonstrated in this image:
You can obtain all jQuery events using $._data($('[selector]')[0],'events'); change [selector] to what you need.
There is a plugin that gather all events attached by jQuery called eventsReport.
Also i write my own plugin that do this with better formatting.
But anyway it seems we can't gather events added by addEventListener method. May be we can wrap addEventListener call to store events added after our wrap call.
It seems the best way to see events added to an element with dev tools.
But you will not see delegated events there. So there we need jQuery eventsReport.
UPDATE: NOW We CAN see events added by addEventListener method SEE RIGHT ANSWER TO THIS QUESTION.

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.

Apply on DOM event with event parameter

Here the case: I want to call the event with apply method, since it's look the one with the best compatibility with any browser.
my_object["onchange"].apply(my_object, event)
So, the event parameter doesn't exist when you define online, example :
<select id="my_select" onchange="my_onchange(event);" >
The event object is create by the engine, but what if I call it with apply?
If I try my_object["onchange"].apply(my_object) the event parameter is not build. Normal because it's call like a function.
So any idea to do build the event?
update
I have try
var event = new Event("onchange", {target:my_object});
It's look like target still readonly even with constructor.
So my workaround is
var event = document.createEvent("HTMLEvents");
event.initEvent("change", false, true);
my_object.dispatchEvent(event);
it's look like it's compatible with all browser and generate the good event parameter.
Thanks

Removing DOM Event Observer

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

Categories

Resources