JavaScript: Get all elements which have an event bind to them - javascript

Is there a way in JavaScript to get all elements on the page which have an event bind to them.
I know you can get all events an a particular event, but is there a way the all elements?

Thank you all for your help. I found this great snippet which loops over all elements on page and adds each elements which has an event bind to it to an array.
var items = Array.prototype.slice.call(
document.querySelectorAll('*')
).map(function(element) {
var listeners = getEventListeners(element);
return {
element: element,
listeners: Object.keys(listeners).map(function(k) {
return { event: k, listeners: listeners[k] };
})
};
}).filter(function(item) {
return item.listeners.length;
});
Full credit to Dan: https://gist.github.com/danburzo/9254630

As suggested in the comments, you can use getEventListeners(document) on chrome dev tools, but if you want to use this inside your code, a possible solution is using a custon function to register the events and mantaining a list of elements that have events atached.
Code not ready to use, just an example
let elements = [];
function addEventListener(elem, event, cb) {
elem.addEventListener(event, cb);
elements.push(elem);
}
You will of course need to remove the element when the event is removed.
If you don't have control over all code that register events (which is quite common) you can monkey path the Node.prototype.addEventListener function. Which is not recomended (you can read more here). But it is a possibility...

Related

Remove click event listener set by another script

With the Chrome developer tools I have found a click event listener I'd like to remove:
If I remove the listener with the developer tools it works. Now I've figured out that the listener is added via jQuery:
$(".js_playerlist").on("click",".playerlist_item",function(){
var a=$(this).hasClass("nothingThere");
if(!a) {
var d=$(this).data("msgid");
if(d) {
b.loadChatLogWithPlayer(this,d)
} else {
b.loadChatLogWithPlayer(this)
}
}
});
How can I remove this event listener via Javascript without jQuery?
You have to jQuery function to do this because the event is attached by jquery so use unbind() or off() the both functions remove the event :
$(".js_playerlist").delay(1000).off("click",".playerlist_item");
//OR
$(".js_playerlist").delay(1000).unbind( "click.playerlist_item" );
You could use a javascript method removeEventListener() but you have to pass the function you want to remove as parameter and the way that the script attaching the event in your case avoid that.
Hope this helps.
I guess you can use
document.getElementsByClassName("js_playerlist")[0].removeEventListener("click", attachedFunction);
In this case attachedFunction is the function that is attached to be called when the event triggers. In your case:
function(){
var a=$(this).hasClass("nothingThere");
if(!a) {
var d=$(this).data("msgid");
if(d) {
b.loadChatLogWithPlayer(this,d)
} else {
b.loadChatLogWithPlayer(this)
}
}
}
Possibly, if you have access to the code, extract the function and assign it to a variable, the code will be cleaner.
If the other script is not under your onwership, in order to be 100% easy to maintain, I would propose that you send a request for the other script code and parse it to get the desired function, so you will have the latest version all the time.

jQuery two different ids trigger the same function

There are 2 ids #firstTableTotal, #secondTableTotal and 1 function contentchanged. How to make both ids(#firstTableTotal, #secondTableTotal) use the same function(contentchanged). I tried with the following codes but the result is not as expected.
$('#firstTableTotal').trigger('contentchanged');
$('#secondTableTotal').trigger('contentchanged');
$(document).on('contentchanged', '#firstTableTotal #secondTableTotal', function()
{alert("Calculations go here");
});
As mentioned already you are trigerring the event even before binding it... Also another problem is with your selectors... There must be a comma between each ID.. Else the meaning would be a parent child combination.
It should be
'#firstTableTotal, #secondTableTotal'
Right now what you have actually means select the element with ID secondTableTotal which is the child of a element with ID firstTableTotal.. Which is not the case in your code.
Your aim is to target both the elements. So place a comma between them. This makes the selector choose two different elements.
That is because you are triggering the event even before it is bound.
Also use a comma to separate the 2 different selectors
$(document).on('contentchanged', '#firstTableTotal, #secondTableTotal',
If contentchanged is an event:
var myFunc = function(){
alert("Calculations go here");
};
$('#firstTableTotal').on('contentchanged', myFunc);
$('#secondTableTotal').on('contentchanged', myFunc);
//Some time later
$('#firstTableTotal').trigger('contentchanged');
$('#secondTableTotal').trigger('contentchanged');
If contentchanged is in fact a function:
var contentchanged = function(){
alert("Calculations go here");
};
$('#firstTableTotal').on('some_event', contentchanged);
$('#secondTableTotal').on('some_event', contentchanged);
//Some time later
$('#firstTableTotal').trigger('some_event');
$('#secondTableTotal').trigger('some_event');
if you have a function called contentChanged:
var contentchanged= function () { //do something}
then you can simply add a listener to each DOM node.
$('#firstTableTotal').on(eventNameHere, contentchanged);
$('#secondTableTotal').on(eventNameHere, contentchanged);
It is best to attach the listeners to the node directly, that way when the nodes are removed from the DOM, the listeners will also be garbage collected. If you add the listener to the window, like you are currently doing, you will need to manually remove it in order for garbage collection to occur.

Prevent calling a function

I have two parts of scripts.
Part 1 :
$("mySelector").click(function() {
alert('you call me');
})
Part 2 :
$("mySelector").click(function() {
if(myCondition) {
//how can i prevent calling the first function from here ???
}
})
The whole problem, is that i have no access to part1. So i need to unbind the event allready specified in part 1, if myCondition is true, but otherwise i need to call the first function.
Thanks
UPDATE:
Thank you. I didn't know about stopImmediatePropagation(). But i feel, that there must be something like that :)
But actually in my case it doesn't work :(
Please have a look at my site
http://www.tours.am/en/outgoing/tours/%D5%80%D5%B6%D5%A4%D5%AF%D5%A1%D5%BD%D5%BF%D5%A1%D5%B6/Park-Hyatt-Goa/
Under the hotel description tab i have cloud carousel, when i click on not active image (not the front image), as you can see i'm consoling that i stopImmediatePropagation() there, but the event however calls :(
If your handler is registered first, then you can use event.stopImmediatePropagation like this:
$("mySelector").click(function(event) {
if(myCondition) {
event.stopImmediatePropagation();
}
})
Be aware that this will also stop event bubbling, so it will also prevent click handlers on parent elements from being invoked.
Update: If this does not work, then your handler is attached after the one you want to control. This is a problem that makes the solution much more difficult. I suggest seeing if you can bind "before the other guy", otherwise you will have to unbind the existing handler and then conditionally invoke it from within your own by retaining a reference to it. See jQuery find events handlers registered with an object.
No access:
$("#mySelector").click(function() {
alert('you call me');
})
Access:
var myCondition = true, //try false too
fFirstFunction = $("#mySelector").data("events").click[0].handler;
$("#mySelector").unbind("click");
$("#mySelector").click(function() {
if(myCondition) {
alert(myCondition);
} else {
$("#mySelector").click(fFirstFunction);
}
});
Look at this example
You can call
$('mySelector').unbind('click');
to get rid of all the click handlers. If your script is loaded after the other one (which appears to be the case), then that should do it. However note that it does unbind all "click" handlers, so make sure you call that before you add your own handler.
If you can't ensure your handler is attached first, try the following code:
var events = $('mySelector').data("events"); //all handlers bound to the element
var clickEvents = events ? events.click : null;//all click handlers bound to the element
$('mySelector').unbind('click'); //unbind all click handlers
//bind your handler
$("mySelector").click(function(e) {
if (myCondition) {
//do what you want
} else {
//call other handlers
if (clickEvents) {
for (var prop in clickEvents)
clickEvents[prop].call(this, e);
}
}
})
Update:
Above code is for jQuery 1.3.2
Above code is based on internal implementation of jQuery 1.3.2, so please check it carefully once you update jQuery.
return false;
-or-
event.preventDefault();

Enable/Disable events of DOM elements with JS / jQuery

I stuck here with a little problem I have put pretty much time in which is pretty bad compared to its functionality.
I have tags in my DOM, and I have been binding several events to them with jQuery..
var a = $('<a>').click(data, function() { ... })
Sometimes I would like to disable some of these elements, which means I add a CSS-Class 'disabled' to it and I'd like to remove all events, so no events are triggered at all anymore. I have created a class here called "Button" to solve that
var button = new Button(a)
button.disable()
I can remove all events from a jQuery object with $.unbind. But I would also like to have the opposite feature
button.enable()
which binds all events with all handlers back to the element
OR
maybe there is a feature in jQuery that actually nows how to do that?!
My Button Class looks something similar to this:
Button = function(obj) {
this.element = obj
this.events = null
this.enable = function() {
this.element.removeClass('disabled')
obj.data('events', this.events)
return this
}
this.disable = function() {
this.element.addClass('disabled')
this.events = obj.data('events')
return this
}
}
Any ideas? Especially this rebind functionality must be available after disable -> enable
var a = $('<a>').click(data, function() { ... })
I found these sources that did not work for me:
http://jquery-howto.blogspot.com/2008/12/how-to-disableenable-element-with.html
http://forum.jquery.com/topic/jquery-temporarily-disabling-events
-> I am not setting the events within the button class
Appreciate your help.
$("a").click(function(event) {
event.preventDefault();
event.stopPropagation();
return false;
});
Returning false is very important.
Or you could write your own enable and disable functions that do something like:
function enable(element, event, eventHandler) {
if(element.data()[event].eventHandler && !eventHandler) { //this is pseudo code to check for null and undefined, you should also perform type checking
element.bind(event, element.data()[event]);
}
else (!element.data()[event] && eventHandler) {
element.bind(event, element.data()[event]);
element.data({event: eventHandler}); //We save the event handler for future enable() calls
}
}
function disable(element, event) {
element.unbind().die();
}
This isn't perfect code, but I'm sure you get the basic idea. Restore the old event handler from the element DOM data when calling enable. The downside is that you will have to use enable() to add any event listener that may need to be disable() d. Otherwise the event handler won't get saved in the DOM data and can't be restored with enable() again. Currently, there's no foolproof way to get a list of all event listeners on an element; this would make the job much easier.
I would go on this with different approach:
<a id="link1">Test function</a>
<a id="link2">Disable/enable function</a>
<script type="text/javascript">
$(function() {
// this needs to be placed before function you want to control with disabled flag
$("#link1").click(function(event) {
console.log("Fired event 1");
if ($(this).hasClass('disabled')) {
event.stopImmediatePropagation();
}
});
$("#link1").click(function() {
console.log("Fired event 2");
});
$("#link2").click(function() {
$("#link1").toggleClass("disabled");
});
});
</script>
This may not be what you require, since it may effect also other functions binded into this event later. The alternative may be to modify the functions itself to be more like:
$("#link1").click(function(event) {
console.log("Fired event 1");
if ($(this).hasClass('disabled')) {
return;
}
// do something here
});
if that is an option.
Instead of adding event handler to each element separately, you should use event delegation. It would make much more manageable structure.
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
http://cherny.com/webdev/70/javascript-event-delegation-and-event-hanlders
http://brandonaaron.net/blog/2010/03/4/event-delegation-with-jquery
This why you can just check for class(es) on clicked element , and act accordingly. And you will be able even to re-eanble them , jsut by changing the classes of a tag.
P.S. read the links carefully, so that you can explain it to others later. Event delegation is a very important technique.
You could use an <input type="button"> and then use $("#buttonID").addAttr('disabled', 'disabled'); and $("#buttonID").removeAttr('disabled');. Disabling and enabling will be handled by the browser. You can still restyle it to look like an anchor, if you need that, by removing backgrounds and borders for the button. Be aware though, that some margins and padding might still bugger u in some browsers.

jQuery: Get reference to click event and trigger it later?

I want to wrap an existing click event in some extra code.
Basically I have a multi part form in an accordion and I want to trigger validation on the accordion header click. The accordion code is used elsewhere and I don't want to change it.
Here's what I've tried:
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
//Get reference to original click
var originalClick = currentAccordion.click;
//unbind original click
currentAccordion.unbind('click');
//bind new event
currentAccordion.click(function () {
//Trigger validation
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
//Call original click.
originalClick();
}
});
});
jQuery throws an error because it's trying to do this.trigger inside the originalClick function and I don't think this is what jQuery expects it to be.
EDIT: Updated code. This works but it is a bit ugly!
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
var originalClick = currentAccordion.data("events")['click'][0].handler;
currentAccordion.unbind('click');
currentAccordion.click(function (e) {
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
$.proxy(originalClick, currentAccordion)(e);
}
});
});
I think this:
var originalClick = currentAccordion.click;
Isn't actually doing what you think it is - you're capturing a reference to the jQuery click function, rather than event handler you added, so when you call originalClick() it's equivalent to: $(value).click()
I finally came up with something reliable:
$(".remove").each(function(){
// get all our click events and store them
var x = $._data($(this)[0], "events");
var y = {}
for(i in x.click)
{
if(x.click[i].handler)
{
y[i] = x.click[i].handler;
}
}
// stop our click event from running
$(this).off("click")
// re-add our click event with a confirmation
$(this).click(function(){
if(confirm("Are you sure?"))
{
// if they click yes, run click events!
for(i in y)
{
y[i]()
}
return true;
}
// if they click cancel, return false
return false;
})
})
This may seem a bit weird (why do we store the click events in the variable "y"?)
Originally I tried to run the handlers in x.click, but they seem to be destroyed when we call .off("click"). Creating a copy of the handlers in a separate variable "y" worked. Sorry I don't have an in depth explanation, but I believe the .off("click") method removes the click event from our document, along with the handlers.
http://www.frankforte.ca/blog/32/unbind-a-click-event-store-it-and-re-add-the-event-later-with-jquery/
I'm not a jQuery user, but in Javascript, you can set the context of the this keyword.
In jQuery, you use the $.proxy() method to do this.
$.proxy(originalClick, value);
originalClick();
Personally, I'd look at creating callback hooks in your Accordion, or making use of existing callbacks (if they exist) that trigger when opening or closing an accordion pane.
Hope that helps :)
currentAccordion.click is a jQuery function, not the actual event.
Starting with a brute-force approach, what you'd need to do is:
Save references to all the currently bound handlers
Unbind them
Add your own handler, and fire the saved ones when needed
Make sure new handlers bound to click are catched too
This looks like a job for an event filter plugin, but I couldn't find one. If the last point is not required in your application, then it's a bit simpler.
Edit: After some research, the bindIf function shown here looks to be what you'd need (or at least give a general direction)

Categories

Resources