Event handlers attached using traditional element.onclick= handler or HTML <element onclick="handler"> can be retrieved trivially from the element.onclick property from script or in-debugger.
Event handlers attached using DOM Level 2 Events addEventListener methods and IE's attachEvent cannot currently be retrieved from script at all. DOM Level 3 once proposed element.eventListenerList to get all listeners, but it is unclear whether this will make it to the final specification. There is no implementation in any browser today.
A debugging tool as browser extension could get access to these kinds of listeners, but I'm not aware of any that actually do.
Some JS frameworks leave enough of a record of event binding to work out what they've been up to. Visual Event takes this approach to discover listeners registered through a few popular frameworks.
You can view directly attached events (element.onclick = handler) by looking at the DOM.
You can view jQuery-attached events in Firefox using FireBug with FireQuery. There doesn't appear to be any way to see addEventListener-added events using FireBug. However, you can see them in Chrome using the Chrome debugger.
You can use Visual Event by Allan Jardine to inspect all the currently attached event handlers from several major JavaScript libraries on your page. It works with jQuery, YUI and several others.
Visual Event is a JavaScript bookmarklet so is compatible with all major browsers.
You can extend your javascript environment to keep track of event listeners. Wrap (or 'overload') the native addEventListener() method with some code that cans keep a record of any event listener added from then onwards. You'd also have to extend HTMLElement.prototype.removeEventListener to keep records that accurately reflect what is happening in the DOM.
Just for the sake of illustration (untested code) - this is an example of how you would 'wrap' addEventListener to have records of the registered event listeners on object itself:
var nativeMethod = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function (type, listener) {
var el = e.currentTarget;
if(!(el.eventListeners instanceof Array)) { el.eventListeners = []}
el.eventListeners.push({'type':type, 'listener':listener});
nativeMethod.call(el, type, listener);
}
I was curious whether @Rolf's approach would actually work. Remember, it is a "crude" way of replacing the standard HTMLElement.prototype.addEventLister() with a wrapped version of the same. Obviously this can only be an "injection method for testing" and would definitely have to be removed for anything approaching the "production version".
When tesing it I found out that, apart from a minor glitch (his e was not defined anywhere but could easily be replaced by a this) the approach does work, as long as
you are consistently working with addEventListener()only on the actual elements themselves
and if you do not use delegated event attachment
or direct event assignments by setting attributes like onclick or oninput.
I went on to find out whether the "sniffing" could be made a little more universal and came up with the following modified version:
(nativeMethod=>{ // IIFE-closure to manipulate the standard addEventListener method:
HTMLElement.prototype.addEventListener = function (type,fun) {
(this.ELL=this.ELL||[]).push([type,fun]);
nativeMethod.call(this,type,fun);
}
})(HTMLElement.prototype.addEventListener);
// LIST direct and indirect event attachments for element `el`:
function listELfor(el){
const events="click,change,input,keyup,keydown,blur,focus,mouseover,mouseout"
.split(",").map(e=>"on"+e); // possible direct event assignments to check up on
const evlist = (el.ELL||[]).map(([t,f])=>[t,f.toString()]);
events.forEach(e=> el[e] && (evlist[e]=[e.substr(2),el[e].toString()]) )
let p=el.parentNode;
if (p.tagName!=="HTML"){ // if available: run function on parent level recursively:
evlist[p.tagName+(p.id?'#'+p.id:'')+(p.className?'.'+p.className:'')]=listELfor(el.parentNode);
}
return evlist;
};
// ============ TESTING ==========================================
// now, let's do some sample event attachments in different ways:
const sp=document.querySelector('h1 span'); // sp = the target SPAN within H1
sp.addEventListener('click',function(e){console.log('first:',e.target)});
sp.addEventListener('click',function(e){console.log('second:',e.target.tagName)});
sp.addEventListener('click',function(e){console.log('third:',e.target.dataset.val)});
// attach an event to the parent node (H1):
sp.parentNode.addEventListener('click',function(e){console.log('Click event attached to H1, click-target is',e.target.tagName);});
// and finally, let's also assign an onclick event directly by using the ONCLICK attribute:
sp.onclick=e=>console.log('direct onclick on span, text:',e.target.textContent);
// Get all event handler functions linked to `sp`?
const allHandlers=listELfor(sp);
for (id in allHandlers) console.log(id,allHandlers[id]);
<div id="main-frame-error" class="interstitial-wrapper">
<div id="main-content">
<div class=""></div>
<div id="main-message">
<h1>Hello, <span data-val="123">THESE WORDS ARE CLICKABLE</span></h1>
<p>Some more text here to pad it out. This text should be unresponsive.</p>
</div>
</div>
</div>
The IIFE structure captures .addEventListener() function handler attachments as an array store in the ELL attribute of the concerned DOM element. The function listELfor(el) then picks up these function handlers of the element itself and walk up the parent hierarchy to also get assignments to its parents. The function will also take care of direct event assignments using onclick and similar attributes.
listELfor() will return an array object with extra properties. These properties will not necessarily be visible in a plain console.log(). This is the reason why I used the for (id in allHandlers) loop.
Please note:
Chrome will list these "extra" Array attributes too - and even further properties, relating to the parent's and their parent's parent's event attachments, like shown below: