I need to find which event handlers are registered over an object.
For example:
$("#el").click(function() {...});
$("#el").mouseover(function() {...});
$("#el") has click and mouseover registered.
Is there a function to find out that, and possibly iterate over the event handlers?
If it is not possible on a jQuery object through proper methods, is it possible on a plain DOM object?
As of jQuery 1.8, the event data is no longer available from the "public API" for data. Read this jQuery blog post. You should now use this instead:
jQuery._data( elem, "events" );
elem should be an HTML Element, not a jQuery object, or selector.
Please note, that this is an internal, 'private' structure, and shouldn't be modified. Use this for debugging purposes only.
In older versions of jQuery, you might have to use the old method which is:
jQuery( elem ).data( "events" );
You can do it by crawling the events (as of jQuery 1.8+), like this:
$.each($._data($("#id")[0], "events"), function(i, event) {
// i is the event type, like "click"
$.each(event, function(j, h) {
// h.handler is the function being called
});
});
Here's an example you can play with:
$(function() {
$("#el").click(function(){ alert("click"); });
$("#el").mouseover(function(){ alert("mouseover"); });
$.each($._data($("#el")[0], "events"), function(i, event) {
output(i);
$.each(event, function(j, h) {
output("- " + h.handler);
});
});
});
function output(text) {
$("#output").html(function(i, h) {
return h + text + "<br />";
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="el">Test</div>
<code>
<span id="output"></span>
</code>
For jQuery 1.8+, this will no longer work because the internal data is placed in a different object.
The latest unofficial (but works in previous versions as well, at least in 1.7.2) way of doing it now is -
$._data(element, "events")
The underscore ("_") is what makes the difference here. Internally, it is calling $.data(element, name, null, true), the last (fourth) parameter is an internal one ("pvt").
Shameless plug, but you can use findHandlerJS
To use it you just have to include findHandlersJS (or just copy&paste the raw javascript code to chrome's console window) and specify the event type and a jquery selector for the elements you are interested in.
For your example you could quickly find the event handlers you mentioned by doing
findEventHandlers("click", "#el")
findEventHandlers("mouseover", "#el")
This is what gets returned:
element
The actual element where the event handler was registered in
events
Array with information about the jquery event handlers for the event type that we are interested in (e.g. click, change, etc)
handler
Actual event handler method that you can see by right clicking it and selecting Show function definition
selector
The selector provided for delegated events. It will be empty for direct events.
targets
List with the elements that this event handler targets. For example, for a delegated event handler that is registered in the document object and targets all buttons in a page, this property will list all buttons in the page. You can hover them and see them highlighted in chrome.
You can try it here
I use eventbug plugin to firebug for this purpose.
I've combined both solutions from #jps to one function:
jQuery.fn.getEvents = function() {
if (typeof(jQuery._data) === 'function') {
return jQuery._data(this.get(0), 'events') || {};
}
// jQuery version < 1.7.?
if (typeof(this.data) === 'function') {
return this.data('events') || {};
}
return {};
};
But beware, this function can only return events that were set using jQuery itself.
To check for events on an element:
var events = $._data(element, "events")
Note that this will only work with direct event handlers, if you are using $(document).on("event-name", "jq-selector", function() { //logic }), you will want to see the getEvents function at the bottom of this answer
For example:
var events = $._data(document.getElementById("myElemId"), "events")
or
var events = $._data($("#myElemId")[0], "events")
Full Example:
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
<script>
$(function() {
$("#textDiv").click(function() {
//Event Handling
});
var events = $._data(document.getElementById('textDiv'), "events");
var hasEvents = (events != null);
});
</script>
</head>
<body>
<div id="textDiv">Text</div>
</body>
</html>
A more complete way to check, that includes dynamic listeners, installed with $(document).on
function getEvents(element) {
var elemEvents = $._data(element, "events");
var allDocEvnts = $._data(document, "events");
for(var evntType in allDocEvnts) {
if(allDocEvnts.hasOwnProperty(evntType)) {
var evts = allDocEvnts[evntType];
for(var i = 0; i < evts.length; i++) {
if($(element).is(evts[i].selector)) {
if(elemEvents == null) {
elemEvents = {};
}
if(!elemEvents.hasOwnProperty(evntType)) {
elemEvents[evntType] = [];
}
elemEvents[evntType].push(evts[i]);
}
}
}
}
return elemEvents;
}
Example usage:
getEvents($('#myElemId')[0])
As of 1.9 there is no documented way to retrieve the events, other than to use the Migrate plugin to restore the old behavior. You could use the _.data() method as jps mentions, but that is an internal method. So just do the right thing and use the Migrate plugin if you need this functionality.
From the jQuery documentation on .data("events")
Prior to 1.9, .data("events") could be used to retrieve jQuery's
undocumented internal event data structure for an element if no other
code had defined a data element with the name "events". This special
case has been removed in 1.9. There is no public interface to retrieve
this internal data structure, and it remains undocumented. However,
the jQuery Migrate plugin restores this behavior for code that depends
upon it.
I created a custom jQuery selector that checks against both jQuery's cache of assigned event handlers as well as elements that use the native method for adding them:
(function($){
$.find.selectors[":"].event = function(el, pos, match) {
var search = (function(str){
if (str.substring(0,2) === "on") {str = str.substring(2);}
return str;
})(String(match[3]).trim().toLowerCase());
if (search) {
var events = $._data(el, "events");
return ((events && events.hasOwnProperty(search)) || el["on"+search]);
}
return false;
};
})(jQuery);
Example:
$(":event(click)")
This will return elements that have a click handler attached to them.
In a modern browser with ECMAScript 5.1 / Array.prototype.map, you can also use
jQuery._data(DOCUMENTELEMENT,'events')["EVENT_NAME"].map(function(elem){return elem.handler;});
in your browser console, which will print the source of the handlers, comma delimited. Useful for glancing at what all is running on a particular event.
Events can be retrieved using:
jQuery(elem).data('events');
or jQuery 1.8+:
jQuery._data(elem, 'events');
Note:
Events bounded using $('selector').live('event', handler)
can be retrieved using:
jQuery(document).data('events')
jQuery is not letting you just simply access the events for a given element.
You can access them using undocumented internal method
$._data(element, "events")
But it still won't give you all the events, to be precise won't show you events assigned with
$([selector|element]).on()
These events are stored inside document, so you can fetch them by browsing through
$._data(document, "events")
but that is hard work, as there are events for whole webpage.
Tom G above created function that filters document for only events of given element and merges output of both methods, but it had a flaw of duplicating events in the output (and effectively on the element's jQuery internal event list messing with your application).
I fixed that flaw and you can find the code below. Just paste it into your dev console or into your app code and execute it when needed to get nice list of all events for given element.
What is important to notice, element is actually HTMLElement, not jQuery object.
function getEvents(element) {
var elemEvents = $._data(element, "events");
var allDocEvnts = $._data(document, "events");
function equalEvents(evt1, evt2)
{
return evt1.guid === evt2.guid;
}
for(var evntType in allDocEvnts) {
if(allDocEvnts.hasOwnProperty(evntType)) {
var evts = allDocEvnts[evntType];
for(var i = 0; i < evts.length; i++) {
if($(element).is(evts[i].selector)) {
if(elemEvents == null) {
elemEvents = {};
}
if(!elemEvents.hasOwnProperty(evntType)) {
elemEvents[evntType] = [];
}
if(!elemEvents[evntType].some(function(evt) { return equalEvents(evt, evts[i]); })) {
elemEvents[evntType].push(evts[i]);
}
}
}
}
}
return elemEvents;
}
I have to say many of the answers are interesting, but recently I had a similar problem and the solution was extremely simple by going the DOM way. It is different because you don't iterate but aim directly at the event you need, but below I'll give a more general answer.
I had an image in a row:
<table>
<td><tr><img class="folder" /></tr><tr>...</tr></td>
</table>
And that image had a click event handler attached to it:
imageNode.click(function () { ... });
My intention was to expand the clickable area to the whole row, so I first got all images and relative rows:
tableNode.find("img.folder").each(function () {
var tr;
tr = $(this).closest("tr");
// <-- actual answer
});
Now in the actual anwer line I just did as follows, giving an answer to the original question:
tr.click(this.onclick);
So I fetched the event handler directly from the DOM element and put it into the jQuery click event handler. Works like a charm.
Now, to the general case. In the old pre-jQuery days you could get all events attached to an object with two simple yet powerful functions gifted to us mortals by Douglas Crockford:
function walkTheDOM(node, func)
{
func(node);
node = node.firstChild;
while (node)
{
walkTheDOM(node, func);
node = node.nextSibling;
}
}
function purgeEventHandlers(node)
{
walkTheDOM(node, function (n) {
var f;
for (f in n)
{
if (typeof n[f] === "function")
{
n[f] = null;
}
}
});
}
Try jquery debugger plugin if you're using chrome: https://chrome.google.com/webstore/detail/jquery-debugger/dbhhnnnpaeobfddmlalhnehgclcmjimi?hl=en
Another way to do it is to just use jQuery to grab the element, then go through actual Javascript to get and set and play with the event handlers. For instance:
var oldEventHandler = $('#element')[0].onclick;
// Remove event handler
$('#element')[0].onclick = null;
// Switch it back
$('#element')[0].onclick = oldEventHandler;
I combined some of the answers above and created this crazy looking but functional script that lists hopefully most of the event listeners on the given element. Feel free to optimize it here.
var element = $("#some-element");
// sample event handlers
element.on("mouseover", function () {
alert("foo");
});
$(".parent-element").on("mousedown", "span", function () {
alert("bar");
});
$(document).on("click", "span", function () {
alert("xyz");
});
var collection = element.parents()
.add(element)
.add($(document));
collection.each(function() {
var currentEl = $(this) ? $(this) : $(document);
var tagName = $(this)[0].tagName ? $(this)[0].tagName : "DOCUMENT";
var events = $._data($(this)[0], "events");
var isItself = $(this)[0] === element[0]
if (!events) return;
$.each(events, function(i, event) {
if (!event) return;
$.each(event, function(j, h) {
var found = false;
if (h.selector && h.selector.length > 0) {
currentEl.find(h.selector).each(function () {
if ($(this)[0] === element[0]) {
found = true;
}
});
} else if (!h.selector && isItself) {
found = true;
}
if (found) {
console.log("################ " + tagName);
console.log("event: " + i);
console.log("selector: '" + h.selector + "'");
console.log(h.handler);
}
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent-element">
<span id="some-element"></span>
</div>
I have a series of JQuery events, as an example, below
<script>
$("#target").click(function() {
..
});
$("#anothertarget").mouseout(function() {
...
});
$("#someselector").scroll(function() {
...
});
... other JQuery events
</script>
How do I "unbind" all these events from the document so they will all stop working and re-bind them all again later without hard coding them ?
There's a couple things you can do but either way you'd need to set conditions for the events:
You could create an event that would have a conditional that will turn the event on or off.
You could set a variable within the condition that would be either true or false, and then have that variable lead to a bind or unbind event.
if(some condition is true){
$("#target").on("click", function() {
});
}
//your scenario may not fit this code exactly but you would need to have conditions that bind or unbind events
var temp = true;
if(some condition is true){
$("#target").on("click", function() {
temp = false
});
};
if (temp == false){
$('#target').off("click",function(){
})
};
//the answer below by JagsSparrow is a pretty good way too
You can create bindAll & unBindAll functions and call them dynamically whenever required.
function bindAll(){
$("#target").click(function() {
..
});
$("#anothertarget").mouseout(function() {
...
});
$("#someselector").scroll(function() {
...
});
}
function unBindAll(){
$("#target").off('click');
$("#anothertarget").off('mouseout');
$("#someselector").off('scroll');
}
//To bind function call
bindAll();
//To unbind function call
unBindAll();
EDIT:
We can store object of events and bind and unbind them as below
var allEvents = {
'#target':{
event:'click',
func:function(){
console.log('click')
}
},
'#anothertarget':{
event:'mouseout',
func:function(){
console.log('mouseout')
}
},
'#someselector':{
event:'scroll',
func:function(){
console.log('scroll')
}
}
}
function bindUnbindAll(isBind){
for(var selector in allEvents){
// Here we can carefully filter some events to bind and unbind
var obj = allEvents[selector];
if(isBind)
$(selector).on(obj.event,obj.func.bind(this));
else
$(selector).off(obj.event);
}
}
//to bind function call
bindUnbindAll(true);
//To unbind function call
bindUnbindAll(false);
To unbind Off, unbind can be used to remove the event, like:
$('#target').unbind('click');
To bind
$('#target').bind('click', function() { /* Do stuff */ });
You can put code in functions to bind/unbind events on your objects. Hope this helps!
I have html grid table consisting of comment link in each row.Clicking on any one opens a bootstrap modal with textbox and save button.So I wrote a library consisting of functions related to that comment system.Below is basic code.
HTML :
<td><a class="addComment" data-notedate="somevalue" data-toggle='modal' href='#addnotesdiv' data-oprid="somevalue" data-soid="somevalue" data-type="1"><i class="fa fa-comments-o fa-2"></i></a></td> ..... n
JS :
var Inventory={};
Inventory.notes={
defaults:{
type:'1',
soid:0,
operator_id:0,
date:'',
target:'div#addnotesdiv',
},
init:function()
{
var self=this;
$('div#addnotesdiv').on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
self.getNotes();
self.addnote();
self.activaterefresh();
});
},
getandsetdefaults:function(e)
{
this.defaults.soid = $(e.relatedTarget).data('soid');
this.defaults.operator_id=$(e.relatedTarget).data('oprid');
this.defaults.type=$(e.relatedTarget).data('type');
this.defaults.date=$(e.relatedTarget).data('notedate');
},
setmodalelements:function(e)
{
$(e.currentTarget).find('#notesthread').empty();
$(e.currentTarget).find('input#inpnotesoid').val(this.defaults.soid);
$(e.currentTarget).find('input#inpnoteoprid').val(this.defaults.operator_id);
$(e.currentTarget).find('input#inpnotetype').val(this.defaults.type);
},
addnote:function()
{
var self=this;
$('button#btnaddnote').on('click',function(){
var message=$(self.defaults.target).find('textarea#addnotemsg').val();
var soid=$(self.defaults.target).find('input[type=hidden][id=inpnotesoid]').val();
var note_date=$(self.defaults.target).find('input#addnotedate').val();
var oprid=$(self.defaults.target).find('input[type=hidden][id=inpnoteoprid]').val();
var type=$(self.defaults.target).find('input[type=hidden][id=inpnotetype]').val();
if(message=="" || soid=="" || note_date=="")
{
alert("Fill all details");
return;
}
var savenote=$.post(HOST+'notes/save',{message:message,soid:soid,note_date:note_date,type:type,operator_id:oprid});
savenote.done(function(res){
res=$.parseJSON(res);
if(res.status && res.error){
alert(res.message);
return;
}
if(res.status && res.type)
{
$('div#addnotemsg').showSuccess("Done").done(function(){self.getNotes();});
$('div#addnotesdiv').find('textarea#addnotemsg').val('');
}
else
{
$('div#addnotemsg').showFailure("Error");
}
});
});
},
getNotes:function()
{
$('button#btnrefreshcomments i').addClass('glyphicon-refresh-animate');
var getnotes=$.getJSON(HOST,{soid:this.defaults.soid,type:this.defaults.type,note_date:this.defaults.date,operator_id:this.defaults.operator_id});
getnotes.done(function(res){
if(res.status && res.data.length)
{
--somecode---
}
});
},
activaterefresh:function(){
var self=this;
$(document).on('click','#btnrefreshcomments',function(){
$('#notesthread').empty();
self.getNotes();
return false;
});
return false;
}
}
In Order to activate this functionality on that page I wrote
Inventory.notes.init();
Above code works perfectly when I open modal once but when I close that same modal and open it again but by clicking on different link all events are fired twice,thrice and so on.Number of events fired is equal to number of times modal opened on that page.
Is there any thing wrong in code Or any other way to perform this same task.
I know this is not a plugin all I wanted was to store all functionality related to comment system under one roof as library.
every time you open the modal box, it triggered show.bs.modal event, then all methods was exec again, including the event bindings. e.g. event bind in [addnote]
$('div#addnotesdiv').on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
self.getNotes();
self.addnote();
self.activaterefresh();
});
Problem was whenever modal was shown getNotes,addnote,activatereferesh functions were called but when the modal was reopened again this functions are called again so thats twice and so on.Putting it in more simpler way is there were multiple listeners attached to single element without destroying previous one because my init function was called many times.
At last there were two solutions in both I need to unbind events or attach them only once.Got idea from here
1) Modified Init function with below code and added one unbind listener function
init:function(selector)
{
var self=this;
$(self.defaults.target).on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
self.getNotes();
self.addnote();
self.activaterefresh();
});
$(self.defaults.target).on('hide.bs.modal',function(e){
self.unbindlistners();
});
}
unbindlistners:function()
{
var self=this;
$('#btnrefreshcomments').unbind('click');
$('button#btnaddnote').unbind('click');
return false;
}
}
2) Place event binding function outside show.bs.modal
init:function(selector)
{
var self=this;
$(self.defaults.target).on('show.bs.modal',function(e){
self.getandsetdefaults(e);
self.setmodalelements(e);
});
self.getNotes();
self.addnote();
self.activaterefresh();
}
There is small catch in second solution that is when first time my DOM is loaded function getNotes function is called with default values.
I have a problem with event object passed to the function in drop event. In my code, div#dropArea has it's drop event handled by firstDrop function which does some animations and then calls the proper function dropFromDesktop which handles the e.dataTransfer.files object. I need this approach in two separate functions because the latter is also used further by some other divs in the HTML document (no need to duplicate the code). First one is used only once, to hide some 'welcome' texts.
Generally, this mechanism lets you drag files from desktop and drop them into an area on my website.
Here's, how it looks (in a shortcut):
function firstDrop(ev) {
var $this = $(this);
//when I call the function here, it passes the event with files inside it
//dropFromDesktop.call($this, ev);
$this.children('.welcomeText').animate({
opacity: '0',
height: '0'
}, 700, function() {
$('#raw .menu').first().slideDown('fast', function() {
//when I call the function here, it passes the event, but 'files' object is empty
dropFromDesktop.call($this, ev);
});
});
}
function dropFromDesktop(ev) {
var files = ev.originalEvent.dataTransfer.files;
(...) //handling the files
}
$('#dropArea').one('drop', firstDrop);
$('some_other_div').on('drop', dropFromDesktop);
The problem is somewhere in jQuery.animation's callback - when I call my function inside it, the event object is passed correctly, but files object from dataTransfer is empty!
Whole script is put inside $(document).ready(function() { ... }); so the order of function declarations doesn't matter, I guess.
I suspect your problem is related with the lifetime of the Event object. Unfortunately, I have no clue about the cause of it. But, there is a way to workaround it that I can think of and it is keeping a reference to Event.dataTransfer.files instead.
var handleFileList = function(fn) {
return function(evt) {
evt.preventDefault();
return fn.call(this, evt.originalEvent.dataTransfer.files);
};
};
var firstDrop = function(fileList) { ... }
var dropFromDesktop = function(fileList) { ... }
$('#dropArea').one('drop', handleFileList(firstDrop));
$('some_other_div').on('drop', handleFileList(dropFromDesktop));
We have multiple animations against the same object. We need to take different actions when each of these animations end.
Right now, we bind to the webkitAnimationEnd event, and use a gnarly if/then statement to handle each animation differently.
Is there a way to essentially create custom webkitAnimationEnd events, allowing us to fire a specific event handler when a specific animation ends? For instance, fire handler1 when animation1 ends and fire handler2 when animation2 ends.
We're building for Webkit browsers, specifically Mobile Safari.
Thanks!
For a simple event-trigger, you can pass a function to jQuery's trigger() method and use the returned value of that function to call a trigger a specific event (which can then be listened-for:
function animEndTrigger(e) {
if (!e) {
return false;
}
else {
var animName = e.originalEvent.animationName;
return animName + 'FunctionTrigger';
}
}
$('body').on('bgAnimFunctionTrigger fontSizeFunctionTrigger', function(e){
console.log(e);
});
$('div').on('webkitAnimationEnd', function(e) {
$(this).trigger(animEndTrigger(e));
});
JS Fiddle demo.
You can, of course, also use the called function to either trigger the event itself or assess the passed parameters to determine whether or not to return an event at all:
One method to assess for a particular event to trigger is to use an object:
var animations = {
'bgAnim': 'aParticularEvent'
};
function animEndTrigger(e) {
if (!e) {
return false;
}
else {
var animName = e.originalEvent.animationName;
return animations[animName] ? animations[animName] : false;
}
}
$('body').on('aParticularEvent', function(e) {
console.log(e);
});
$('div').on('webkitAnimationEnd', function(e) {
$(this).trigger(animEndTrigger(e));
});
JS Fiddle demo.
Though, in this case, the return false should be altered so as not to provide the error Uncaught TypeError: Object false has no method 'indexOf' (which I've not bothered, as yet, to account for).
The following causes the called-function (animEndTrigger()) to directly trigger() the custom event (which requires an element on which to bind the trigger() method) and also avoids the Uncaught TypeError above:
var animations = {
'bgAnim': 'aParticularEvent'
};
function animEndTrigger(e, el) {
if (!e || !el) {
return false;
}
else {
var animName = e.originalEvent.animationName;
if (animations[animName]) {
$(el).trigger(animations[animName]);
}
}
}
$('body').on('aParticularEvent', function(e) {
console.log(e);
});
$('div').on('webkitAnimationEnd', function(e) {
animEndTrigger(e, this);
});
JS Fiddle demo.
Of course you're still, effectively, using an if to perform an assessment, so I can't be particularly sure that this is any tidier than your own already-implemented solution.