Removing event listeners added by class - javascript

I would like to understand how to remove the event listeners which are added by class as shown below (remove event for div#mainMenu)
I have tried all available methods but none to my avail. Also I dont see this issue in the extJS4.x version but only in 3.4 version. Any plain JS code to handle this?

You can use getEventListeners function to get listeners map of an element. After that use removeEventListener method, to remove event from element. You can use this function to remove all events from an element.
function removeEventListeners(element, listenerMap) {
Object.keys(listenerMap).forEach(function (name) {
var listeners = listenerMap[name];
listeners.forEach(function (object) {
element.removeEventListener(name, object.listener);
});
});
}
removeEventListeners(elementRef,getEventListeners(elementRef));

Finally, I have figured out how to resolve this issue. The way to remove the listener is to go to parent div component & call removeAllListeners() method on the element. The element can be accessed in the afterRenderHandler function using this.el & then calling this.el.parent().removeAllListeners() so that it removes the un-required listeners interfering with the user experience. You could call the parent method in a chained fashion if the listener identified is a nth level parent (call the method n times).
Ex: this.el.parent().parent().removeAllListeners()
You could also try to access the element directly if you have the ID by calling: var el = Ext.get('mainMenu'); & then calling el.removeAllListeners();
Similar approach can be applied to any other JS frameworks by getting a handle to the element having an issue with the listeners.

Related

Remove already existing event listener

I'm trying to remove an already existing event listener from an object. I've tried several methods (using jQuery .off(), .unbind(), standard removeEventsListeners()), but as stated in the docs they can only remove previously added ones?
Using the Chrome debugger, I can see and remove the specified event listener,
Also, when trying to list the event listeners via jQuery _data() function, it won't list the event. Have been searching for an answer for a couple of hours now.
Can anyone help? Any workaround?
Edit: I have to keep some, so cloning is not possible.
If the event handler was added with addEventListener, you cannot remove it unless you have a reference to the handler function that was added. I assume that must be the case, because if it were hooked up with jQuery's on (or various shortcuts for it), off would work, and you've said it didn't work.
One way to work around that is to replace the element with a clone. When you clone an element using the DOM's cloneNode, you don't copy its event handlers. So if you start out with element, then you can clone it, use insertBefore to insert the clone, then use removeChild to remove the original:
var newElement = element.cloneNode(true); // true = deep
element.parentNode.insertBefore(newElement, element);
element.parentNode.removeChild(element);
Of course, this is indeed a workaround. The proper thing would be not to set up the handler in the first place, or keep a reference to it if you need to be able to remove it later.
In a comment you've said you can't do that, and asked:
Is there a way to add a new event listener that blocks the already existing one?
Only if you can get there first, otherwise no, you can't.
You can't add a new handler that blocks an existing one (not in a standard way cross-browser), but if you can add yours before the other one is added, you can prevent it being called by using stopImmediatePropagation:
// Your handler
document.getElementById("target").addEventListener("click", function(e) {
console.log("Your handler");
e.stopImmediatePropagation();
e.stopPropagation();
});
// The one that you're trying to prevent
document.getElementById("target").addEventListener("click", function(e) {
console.log("Handler you're trying to prevent");
});
<div id="target">Click me</div>
So for instance, if the other handler is added in the window load event, or a jQuery ready handler, you may be able to get yours in first by putting your code in a script tag immediately after the element in question.
A very easy way of doing this is to append nothing to the parent element with .innerHTML. Just be aware this destroys all event listeners that are descendants of the parent element. Here's an example (click 2 to destroy the event listener attached to 1):
const d1 = document.querySelector('#d1');
d1.addEventListener('click', () => console.log(123));
const d2 = document.querySelector('#d2');
d2.addEventListener('click', () => d1.parentNode.innerHTML += '');
<div><button id="d1">1</button></div>
<div><button id="d2">2</button></div>

Passing an jQuery element cache to a method for modification

Problem Fiddle:
Click To View (Attempt #1, using Delegated Events)
Click To View (Attempt #2, brute force approach as below)
Click To View (Attempt #3, refactored, has problem I am trying to solve)
On a project I'm working with, I'm exploring a rather dynamic form. In addition to some static elements, there are various interactive elements, which can be cloned from a hidden 'template' markup set and added at various points in the business process.
Because of the dynamic nature, my tried-and-true method of setting up a jQuery element cache and event handlers on-load, then letting the user do whatever isn't working out, because of this dynamic nature; I was finding that my dynamically-added elements had no click events.
To solve this problem, I manually set up a rebind method for each scripted element in question. The rebind process involves A) re-acquiring the set of elements for a given descriptive selector, B) dropping any existing events on that cache, as those events apply to an incomplete element set, and C) calling a bind method to apply the new events to the entire set.
The brute-force, working way that I got, had this going on:
var $elementCache = $('.some-class');
function rebindSomeLink() {
// Re-acquire the element cache...
$elementCache = $('.some-class');
// Drop all existing events on the cache...
$elementCache.unbind();
// Call a bind function to establish new events.
bindSomeLink();
}
function bindSomeLink() {
$elementCache.click(function (e) {
// ...Behavior...
});
}
// There are four other links with a similar rebind/bind function relationship set up.
Naturally, I seized on the rebind being repeated so often with nearly the exact same code - ripe for a refactor. We have a common library namespace, where I added a rebindEvents function...
var MyCommon = function () {
var pub = {};
pub.rebindEvents = function($elementCache, selector, bindFunction) {
$elementCache = $(selector);
$elementCache.unbind();
bindFunction();
};
return pub;
}();
Upon trying to call that, and run the site, I immediately stubbed my toe on an UncaughtTypeError: method click cannot be called on object undefined.
As it turns out, it seems when I call the following:
MyCommon.rebindEvents($elementCache, '.some-class', bindSomeLink);
The $elementCache is not being passed to the rebindEvents method; when I step to it in my debugger, $elementCache inside of rebindEvents is undefined.
Some handy StackOverflow research revealed to me that JavaScript does not have referential-passing, at least in the C/C++/C# sense that I am familiar with, which leads me to my two Questions:
A) Is it even possible for me to refactor this rebind functionality with a cache reference pass of some sort?
B) If it's possible for me to refactor my rebind function to my common namespace, how would I go about doing it?
Use Jquery On to bind events at an element high-up in the DOM that is always present.
http://api.jquery.com/on/
This is the simplest way to handle event binding to dynamically created elements.
here is a jsfiddle that shows an example.
$('#temp').on('click', 'button', function(){
alert('clicked');
});
$('#temp').append('<button>OK</button>');
The event is bound to a div, which later has a button dynamically added. Because the button has no click event, the event "bubbles" up the DOM tree to its parent element which does have a click event for a button, so it handles it and the event "bubbles" no further.

jQuery event delegation

I need some help with the callbacks. For some reason, they don't work really well.
I'm making a game with jQuery. I have a <div id='button'></div> for all the buttons that are going to be in the game. The game is going to have two buttons that make actions, and a question on top of it. The question is controlled by a <h3 id='text'></h3>. What I want to know, is that for some reason I can't set callback functions to the button's ID's. In example,
I'd have the yes or no, that have their own id's set through jQuery like this:
$('#button').html('<button id='yes'>Yes</button><button id='no'></button>');
But for some reason, I would be able to set this:
$('yes').click(function(){
//function I would want
});
Of course, that's not what my code has, that was just an example. Here's the real code:
$(document).ready(function(){
$('#main,#batman,#car,#cop,#hobo,#knife,#gangfight,#ganggun,#gangknife,#blood,#hr').hide(-100);
var hr=$('#hr');
var main=$('#main');
var batman=$('#batman');
var car=$('#car');
var hobo=$('#hobo');
var cop=$('#cop');
var knife=$('#knife');
var gangfight=$('#gangfight');
var ganggun=$('#ganggun');
var gangknife=$('#gangknife');
var blood=$('#blood');
var text=$('#text');
var button=$('#button');
$('#start').html('Are you ready to play?');
$('#button').html('<button id="yes">Yes</button><button id="no">No</button>');
$('#yes').click(function(){
$('#yes,#no').hide(function(){
$('#start').hide();
main.fadeIn(-100);
hr.fadeIn(-100,function(){
text.delay(1000).html("You were just wandering around in the streets of new york, when suddenly.. You see batman!! You've never really liked him, what do you do?")
button.html('<button id="fight">Fight</button><button id="leave">Leave</button>',function(){
batman.fadeIn(1000);
$('fight').click(function(){
});
$('leave').click(function(){
text.fadeOut(function(){
text.text('Good call. As you leave, you encounter a hobo. What do you do?');
});
});
});
});
});
});
$('#no').click(function(){
$('#yes,#no').hide();
$('#start').text('Oh, okay then. Come back later!');
});
});
I'm just wondering.. How can I set callback functions to the 'fight' and 'leave'.
If you're wondering why there's all these variables at the start, those are just the images and characters.
You can't set a click handler on an element that doesn't exist. What you should do is use .on to bind a element further up the tree. Something like:
$("#someparentelement").on("click", "#yes", function() {
// your code
});
Which version of jQuery are you using? You should probably use jQuery.on() in this situation since your click handler code probably gets executed before the button is actually available in the DOM.
$("#button").on("click", "#yes", function (event) {
// Your yes-button logic comes here.
});
For more details and possibilities, read about the .on(events [, selector ] [, data ], handler(eventObject)) method in the jQuery documentation:
If selector is omitted or is null, the event handler is referred to as direct or directly-bound. The handler is called every time an event occurs on the selected elements, whether it occurs directly on the element or bubbles from a descendant (inner) element.
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
In this case, you want to delegate the event since your element is not yet available in the DOM when you're binding the event.
Don't use the click(), use on('click') and attach it to the document.
Creating a handler this way, will ensure that any new elements will be able to trigger the event.
$('fight') selects fight tag, not the tag with fight id. Try to use $('#fight') instead.

Does jQuery overwrite event listeners?

I've got a bunch divs which each contain a remove link attached with the click event below:
var observeRemoveRoom = function
$('.remove_room').click(function(){
$(this).parent().removeClass('active');
});
}
Clicking it removes the 'active' class of the parent (the div). I call this observeRemoveRoom function on window load which works fine.
The thing is, I have another function which adds more of the same divs. Since the a.remove_room links contained within the new divs weren't around on window.load I need to call observeRemoveRoom.
Am I somehow duplicating the event handlers? Does jQuery overwrite them? If so should I unbind the handlers?
Each time you call observeRemoveRoom jQuery will add a new unique event handler function for a click event.
So yes, you need to .unbind() either all currently bound handlers by just calling .unbind() without arguments, or be specific and pass in a function reference.
You can try a live query to keep them updated: http://docs.jquery.com/Plugins/livequery
Yes, you will be duplicating the event-handlers if you call observeRemoveRoom again, but it might not be noticeable since you are only calling the removeClass method which does nothing if the class is not found, which would be the case after the first listener is triggered.
Instead you can un-bind and re-bind the click event each time, like:
var observeRemoveRoom = function(){
var remove_class = function(){
$(this).parent().removeClass('active');
};
$('.remove_room').off('click', remove_class).on('click', remove_class);
}
But that said, it is recommended that you do this outside this function`, rather than binding and unbinding the event every time, like:
$(document).ready(function(){
var remove_class = function(){
$(this).parent().removeClass('active');
};
// If the element exists at dom ready, you can bind the event directly
$('.remove_room').on("click", remove_class);
// If the element is added in dynamically, you can [delegate][1] the event
$('body').on("click", '.remove_room', remove_class);
// Note: Although I've delegated the event to the body tag in this case
// I recommend that you use the closest available parent instead
});
http://api.jquery.com/on/#direct-and-delegated-events : [1]

How do I access what events have been bound to a dom element using only JavaScript, no frameworks

How do I access what events have been bound to a DOM element using JavaScript and NOT using a library/framework or Firefox add-on? Just pure JavaScript. I incorrectly assumed there would be an events object stored as a property of the element which has the binding.
For example if I had bound say, click, dblclick and mouseover to an element how would I do the following NOT using jQuery. Just JavaScript.
function check(el){
var events = $(el).data('events');
for (i in $(el).data('events')) {
console.log(i) //logs click dblclick and mouseover
}
}
I know jQuery stores an events object as a data property i.e. $(el).data('events') and the eventbug add-on displays the event binding so there must be way.
I will also add that this question came about because I read about memory leaks in older IE browsers and how it's best to remove the bound events before removing a node from the DOM, which lead me to think, how can I test for what events are bound to an element?
You can't reliably know what listeners are on an element if you aren't in complete control of the environment. Some libraries manually control event listeners, e.g. jQuery stores them in an object and adds a custom attribute to the element so that when an even occurs, it uses the custom property to look up the listeners for that element and event and calls them.
Try:
<div id="d0">div 0</div>
<script type="text/javascript">
var el = document.getElementById('d0')
el.addEventListener('click',function(){alert('hey');},false);
// Make sure listener has had time to be set then look at property
window.setTimeout(function(){alert(el.onclick)}, 100); // null
</script>
So to know what listeners have been added, you need to be in complete control.
Edit:
To make it clear, there is no reliable way to inspect an element with javascript and discover what listeners have been added, or even if any have been added at all. You can create an event registration scheme to track listeners added using your event registration methods. But if listeners are added some other way (e.g. directly by addEventListener) then your registration scheme won't know about them.
As pointed out elsewhere, jQuery (and others) can't track listeners that are added directly to elements without using the jQuery event registration methods (click, bind, mouseover, etc.) because they use those methods to register (and call) the listeners.
// list of onevent attributes; you'll have to complete this list
var arr = ['onclick', 'onmouseover', 'onmouseup'];
for ( var i = 0; i < arr.length; i++ ) {
if ( el[arr[i]] != null ) {
// element has an arr[i] handler
}
}
where el is a reference to your DOM element
Complete lists are here

Categories

Resources