Inspect attached event handlers for any DOM element - javascript

Is there any way to view what functions / code are attached to any event for a DOM element? Using Firebug or any other tool.

The Elements Panel in Google Chrome Developer tools has had this since Chrome releases in mid 2011 and Chrome developer channel releases since 2010.
Also, the event listeners shown for the selected node are in the order in which they are fired through the capturing and bubbling phases.
Hit command + option + i on Mac OSX and Ctrl + Shift + i on Windows to fire this up in Chrome

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.

Chrome Dev Tools recently announced some new tools for Monitoring JavaScript Events.
TL;DR
Listen to events of a certain type using monitorEvents().
Use unmonitorEvents() to stop listening.
Get listeners of a DOM element using getEventListeners().
Use the Event Listeners Inspector panel to get information on event listeners.
Finding Custom Events
For my need, discovering custom JS events in 3rd party code, the following two versions of the getEventListeners() were amazingly helpful;
getEventListeners(window)
getEventListeners(document)
If you know what DOM Node the event listener was attached to you'd pass that instead of window or document.
Known Event
If you know what event you wish to monitor e.g. click on the document body you could use the following: monitorEvents(document.body, 'click');.
You should now start seeing all the click events on the document.body being logged in the console.

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]);
h1 span {cursor:pointer}
.as-console-wrapper {max-height:85% !important}
<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:

Related

How can I create multiple click events for the same element [duplicate]

What's the difference between addEventListener and onclick?
var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);
The code above resides together in a separate .js file, and they both work perfectly.
Both are correct, but none of them are "best" per se, and there may be a reason the developer chose to use both approaches.
Event Listeners (addEventListener and IE's attachEvent)
Earlier versions of Internet Explorer implement JavaScript differently from pretty much every other browser. With versions less than 9, you use the attachEvent[doc] method, like this:
element.attachEvent('onclick', function() { /* do stuff here*/ });
In most other browsers (including IE 9 and above), you use addEventListener[doc], like this:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
Using this approach (DOM Level 2 events), you can attach a theoretically unlimited number of events to any single element. The only practical limitation is client-side memory and other performance concerns, which are different for each browser.
The examples above represent using an anonymous function[doc]. You can also add an event listener using a function reference[doc] or a closure[doc]:
var myFunctionReference = function() { /* do stuff here*/ }
element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);
Another important feature of addEventListener is the final parameter, which controls how the listener reacts to bubbling events[doc]. I've been passing false in the examples, which is standard for probably 95% of use cases. There is no equivalent argument for attachEvent, or when using inline events.
Inline events (HTML onclick="" property and element.onclick)
In all browsers that support javascript, you can put an event listener inline, meaning right in the HTML code. You've probably seen this:
<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>
Most experienced developers shun this method, but it does get the job done; it is simple and direct. You may not use closures or anonymous functions here (though the handler itself is an anonymous function of sorts), and your control of scope is limited.
The other method you mention:
element.onclick = function () { /*do stuff here */ };
... is the equivalent of inline javascript except that you have more control of the scope (since you're writing a script rather than HTML) and can use anonymous functions, function references, and/or closures.
The significant drawback with inline events is that unlike event listeners described above, you may only have one inline event assigned. Inline events are stored as an attribute/property of the element[doc], meaning that it can be overwritten.
Using the example <a> from the HTML above:
var element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };
... when you clicked the element, you'd only see "Did stuff #2" - you overwrote the first assigned of the onclick property with the second value, and you overwrote the original inline HTML onclick property too. Check it out here: http://jsfiddle.net/jpgah/.
Broadly speaking, do not use inline events. There may be specific use cases for it, but if you are not 100% sure you have that use case, then you do not and should not use inline events.
Modern Javascript (Angular and the like)
Since this answer was originally posted, javascript frameworks like Angular have become far more popular. You will see code like this in an Angular template:
<button (click)="doSomething()">Do Something</button>
This looks like an inline event, but it isn't. This type of template will be transpiled into more complex code which uses event listeners behind the scenes. Everything I've written about events here still applies, but you are removed from the nitty gritty by at least one layer. You should understand the nuts and bolts, but if your modern JS framework best practices involve writing this kind of code in a template, don't feel like you're using an inline event -- you aren't.
Which is Best?
The question is a matter of browser compatibility and necessity. Do you need to attach more than one event to an element? Will you in the future? Odds are, you will. attachEvent and addEventListener are necessary. If not, an inline event may seem like they'd do the trick, but you're much better served preparing for a future that, though it may seem unlikely, is predictable at least. There is a chance you'll have to move to JS-based event listeners, so you may as well just start there. Don't use inline events.
jQuery and other javascript frameworks encapsulate the different browser implementations of DOM level 2 events in generic models so you can write cross-browser compliant code without having to worry about IE's history as a rebel. Same code with jQuery, all cross-browser and ready to rock:
$(element).on('click', function () { /* do stuff */ });
Don't run out and get a framework just for this one thing, though. You can easily roll your own little utility to take care of the older browsers:
function addEvent(element, evnt, funct){
if (element.attachEvent)
return element.attachEvent('on'+evnt, funct);
else
return element.addEventListener(evnt, funct, false);
}
// example
addEvent(
document.getElementById('myElement'),
'click',
function () { alert('hi!'); }
);
Try it: http://jsfiddle.net/bmArj/
Taking all of that into consideration, unless the script you're looking at took the browser differences into account some other way (in code not shown in your question), the part using addEventListener would not work in IE versions less than 9.
Documentation and Related Reading
W3 HTML specification, element Event Handler Attributes
element.addEventListener on MDN
element.attachEvent on MSDN
Jquery.on
quirksmode blog "Introduction to Events"
CDN-hosted javascript libraries at Google
The difference you could see if you had another couple of functions:
var h = document.getElementById('a');
h.onclick = doThing_1;
h.onclick = doThing_2;
h.addEventListener('click', doThing_3);
h.addEventListener('click', doThing_4);
Functions 2, 3 and 4 work, but 1 does not. This is because addEventListener does not overwrite existing event handlers, whereas onclick overrides any existing onclick = fn event handlers.
The other significant difference, of course, is that onclick will always work, whereas addEventListener does not work in Internet Explorer before version 9. You can use the analogous attachEvent (which has slightly different syntax) in IE <9.
In this answer I will describe the three methods of defining DOM event handlers.
element.addEventListener()
Code example:
const element = document.querySelector('a');
element.addEventListener('click', event => event.preventDefault(), true);
Try clicking this link.
element.addEventListener() has multiple advantages:
Allows you to register unlimited events handlers and remove them with element.removeEventListener().
Has useCapture parameter, which indicates whether you'd like to handle event in its capturing or bubbling phase. See: Unable to understand useCapture attribute in addEventListener.
Cares about semantics. Basically, it makes registering event handlers more explicit. For a beginner, a function call makes it obvious that something happens, whereas assigning event to some property of DOM element is at least not intuitive.
Allows you to separate document structure (HTML) and logic (JavaScript). In tiny web applications it may not seem to matter, but it does matter with any bigger project. It's way much easier to maintain a project which separates structure and logic than a project which doesn't.
Eliminates confusion with correct event names. Due to using inline event listeners or assigning event listeners to .onevent properties of DOM elements, lots of inexperienced JavaScript programmers thinks that the event name is for example onclick or onload. on is not a part of event name. Correct event names are click and load, and that's how event names are passed to .addEventListener().
Works in almost all browser. If you still have to support IE <= 8, you can use a polyfill from MDN.
element.onevent = function() {} (e.g. onclick, onload)
Code example:
const element = document.querySelector('a');
element.onclick = event => event.preventDefault();
Try clicking this link.
This was a way to register event handlers in DOM 0. It's now discouraged, because it:
Allows you to register only one event handler. Also removing the assigned handler is not intuitive, because to remove event handler assigned using this method, you have to revert onevent property back to its initial state (i.e. null).
Doesn't respond to errors appropriately. For example, if you by mistake assign a string to window.onload, for example: window.onload = "test";, it won't throw any errors. Your code wouldn't work and it would be really hard to find out why. .addEventListener() however, would throw error (at least in Firefox): TypeError: Argument 2 of EventTarget.addEventListener is not an object.
Doesn't provide a way to choose if you want to handle event in its capturing or bubbling phase.
Inline event handlers (onevent HTML attribute)
Code example:
Try clicking this link.
Similarly to element.onevent, it's now discouraged. Besides the issues that element.onevent has, it:
Is a potential security issue, because it makes XSS much more harmful. Nowadays websites should send proper Content-Security-Policy HTTP header to block inline scripts and allow external scripts only from trusted domains. See How does Content Security Policy work?
Doesn't separate document structure and logic.
If you generate your page with a server-side script, and for example you generate a hundred links, each with the same inline event handler, your code would be much longer than if the event handler was defined only once. That means the client would have to download more content, and in result your website would be slower.
See also
EventTarget.addEventListener() documentation (MDN)
EventTarget.removeEventListener() documentation (MDN)
onclick vs addEventListener
dom-events tag wiki
While onclick works in all browsers, addEventListener does not work in older versions of Internet Explorer, which uses attachEvent instead.
The downside of onclick is that there can only be one event handler, while the other two will fire all registered callbacks.
Summary:
addEventListener can add multiple events, whereas with onclick this cannot be done.
onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
addEventListener can take a third argument which can stop the event propagation.
Both can be used to handle events. However, addEventListener should be the preferred choice since it can do everything onclick does and more. Don't use inline onclick as HTML attributes as this mixes up the javascript and the HTML which is a bad practice. It makes the code less maintainable.
As far as I know, the DOM "load" event still does only work very limited. That means it'll only fire for the window object, images and <script> elements for instance. The same goes for the direct onload assignment. There is no technical difference between those two. Probably .onload = has a better cross-browser availabilty.
However, you cannot assign a load event to a <div> or <span> element or whatnot.
An element can have only one event handler attached per event type, but can have multiple event listeners.
So, how does it look in action?
Only the last event handler assigned gets run:
const button = document.querySelector(".btn")
button.onclick = () => {
console.log("Hello World");
};
button.onclick = () => {
console.log("How are you?");
};
button.click() // "How are you?"
All event listeners will be triggered:
const button = document.querySelector(".btn")
button.addEventListener("click", event => {
console.log("Hello World");
})
button.addEventListener("click", event => {
console.log("How are you?");
})
button.click()
// "Hello World"
// "How are you?"
IE Note: attachEvent is no longer supported. Starting with IE 11, use addEventListener: docs.
One detail hasn't been noted yet: modern desktop browsers consider different button presses to be "clicks" for AddEventListener('click' and onclick by default.
On Chrome 42 and IE11, both onclick and AddEventListener click fire on left and middle click.
On Firefox 38, onclick fires only on left click, but AddEventListener click fires on left, middle and right clicks.
Also, middle-click behavior is very inconsistent across browsers when scroll cursors are involved:
On Firefox, middle-click events always fire.
On Chrome, they won't fire if the middleclick opens or closes a scroll cursor.
On IE, they fire when scroll cursor closes, but not when it opens.
It is also worth noting that "click" events for any keyboard-selectable HTML element such as input also fire on space or enter when the element is selected.
element.onclick = function() { /* do stuff */ }
element.addEventListener('click', function(){ /* do stuff */ },false);
They apparently do the same thing: listen for the click event and execute a callback function. Nevertheless, they’re not equivalent. If you ever need to choose between the two, this could help you to figure out which one is the best for you.
The main difference is that onclick is just a property, and like all object properties, if you write on more than once, it will be overwritten. With addEventListener() instead, we can simply bind an event handler to the element, and we can call it each time we need it without being worried of any overwritten properties.
Example is shown here,
Try it: https://jsfiddle.net/fjets5z4/5/
In first place I was tempted to keep using onclick, because it’s shorter and looks simpler… and in fact it is. But I don’t recommend using it anymore. It’s just like using inline JavaScript. Using something like – that’s inline JavaScript – is highly discouraged nowadays (inline CSS is discouraged too, but that’s another topic).
However, the addEventListener() function, despite it’s the standard, just doesn’t work in old browsers (Internet Explorer below version 9), and this is another big difference. If you need to support these ancient browsers, you should follow the onclick way. But you could also use jQuery (or one of its alternatives): it basically simplifies your work and reduces the differences between browsers, therefore can save you a lot of time.
var clickEvent = document.getElementByID("onclick-eg");
var EventListener = document.getElementByID("addEventListener-eg");
clickEvent.onclick = function(){
window.alert("1 is not called")
}
clickEvent.onclick = function(){
window.alert("1 is not called, 2 is called")
}
EventListener.addEventListener("click",function(){
window.alert("1 is called")
})
EventListener.addEventListener("click",function(){
window.alert("2 is also called")
})
Javascript tends to blend everything into objects and that can make it confusing. All into one is the JavaScript way.
Essentially onclick is a HTML attribute. Conversely addEventListener is a method on the DOM object representing a HTML element.
In JavaScript objects, a method is merely a property that has a function as a value and that works against the object it is attached to (using this for example).
In JavaScript as HTML element represented by DOM will have it's attributes mapped onto its properties.
This is where people get confused because JavaScript melds everything into a single container or namespace with no layer of indirection.
In a normal OO layout (which does at least merge the namespace of properties/methods) you would might have something like:
domElement.addEventListener // Object(Method)
domElement.attributes.onload // Object(Property(Object(Property(String))))
There are variations like it could use a getter/setter for onload or HashMap for attributes but ultimately that's how it would look. JavaScript eliminated that layer of indirection at the expect of knowing what's what among other things. It merged domElement and attributes together.
Barring compatibility you should as a best practice use addEventListener. As other answers talk about the differences in that regard rather than the fundamental programmatic differences I will forgo it. Essentially, in an ideal world you're really only meant to use on* from HTML but in an even more ideal world you shouldn't be doing anything like that from HTML.
Why is it dominant today? It's quicker to write, easier to learn and tends to just work.
The whole point of onload in HTML is to give access to the addEventListener method or functionality in the first place. By using it in JS you're going through HTML when you could be applying it directly.
Hypothetically you can make your own attributes:
$('[myclick]').each(function(i, v) {
v.addEventListener('click', function() {
eval(v.myclick); // eval($(v).attr('myclick'));
});
});
What JS does with is a bit different to that.
You can equate it to something like (for every element created):
element.addEventListener('click', function() {
switch(typeof element.onclick) {
case 'string':eval(element.onclick);break;
case 'function':element.onclick();break;
}
});
The actual implementation details will likely differ with a range of subtle variations making the two slightly different in some cases but that's the gist of it.
It's arguably a compatibility hack that you can pin a function to an on attribute since by default attributes are all strings.
You should also consider EventDelegation for that!
For that reason I prefer the addEventListener and foremost using it carefully and consciously!
FACTS:
EventListeners are heavy .... (memory allocation at the client side)
The Events propagate IN and then OUT again in relation to the DOM
tree. Also known as trickling-in and bubbling-out , give it a read
in case you don't know.
So imagine an easy example:
a simple button INSIDE a div INSIDE body ...
if you click on the button, an Event will ANYWAY
trickle in to BUTTON and then OUT again, like this:
window-document-div-button-div-document-window
In the browser background (lets say the software periphery of the JS engine) the browser can ONLY possibly react to a click, if it checks for each click done where it was targeted.
And to make sure that each possible event listener on the way is triggered, it kinda has to send the "click event signal" all the way from document level down into the element ... and back out again.
This behavior can then made use of by attaching EventListeners using e.g.:
document.getElementById("exampleID").addEventListener("click",(event) => {doThis}, true/false);
Just note for reference that the true/false as the last argument of the addEventListener method controls the behavior in terms of when is the event recognized - when trickling in or when bubbling out.
TRUE means, the event is recognized while trickling-in
FALSE means, the event is recognized on its way bubbling out
Implementing the following 2 helpful concepts also turns out much more intuitive using the above stated approach to handle:
You can also use event.stopPropagation() within the function
(example ref. "doThis") to prevents further propagation of the
current event in the capturing and bubbling phases. It does not,
however, prevent any default behaviors from occurring; for instance,
clicks on links are still processed.
If you want to stop those behaviors, you could use
event.preventDefault() within the function (example ref.
"doThis"). With that you could for example tell the Browser that if
the event does not get explicitly handled, its default action should
not be taken as it normally would be.
Also just note here for reference again: the last argument of the addEventListener method (true/false) also controls at which phase (trickling-in TRUE or bubbling out FALSE) the eventual effect of ".stopPropagation()" kicks in.
So ... in case you apply an EventListener with flag TRUE to an element, and combine that with the .stopPropagation() method, the event would not even get through to potential inner children of the element
To wrap it up:
If you use the onClick variant in HTML ... there are 2 downsides for me:
With addEventListener, you can attach multiple onClick-events to the same, respectively one single element, but thats not possible using onClick (at least thats what I strongly believe up to now, correct me if I am wrong).
Also the following aspect is truly remarkable here ... especially the code maintenance part (didn't elaborate on this so far):
In regards to event delegation, it really boils down to this. If some
other JavaScript code needs to respond to a click event, using
addEventListener ensures you both can respond to it. If you both try
using onclick, then one stomps on the other. You both can't respond if
you want an onclick on the same element. Furthermore, you want to keep your behavior as separate as you can from the HTML in case you need to change it later. It would suck to have 50 HTML files to update instead of one JavaScript file.
(credit to Greg Burghardt, addEventListener vs onclick with regards to event delegation )
This is also known by the term "Unobtrusive JavaScript" ... give it a read!
According to MDN, the difference is as below:
addEventListener:
The EventTarget.addEventListener() method adds the specified
EventListener-compatible object to the list of event listeners for the
specified event type on the EventTarget on which it's called. The
event target may be an Element in a document, the Document itself, a
Window, or any other object that supports events (such as
XMLHttpRequest).
onclick:
The onclick property returns the click event handler code on the
current element. When using the click event to trigger an action, also
consider adding this same action to the keydown event, to allow the
use of that same action by people who don't use a mouse or a touch
screen. Syntax element.onclick = functionRef; where functionRef is a
function - often a name of a function declared elsewhere or a function
expression. See "JavaScript Guide:Functions" for details.
There is also a syntax difference in use as you see in the below codes:
addEventListener:
// Function to change the content of t2
function modifyText() {
var t2 = document.getElementById("t2");
if (t2.firstChild.nodeValue == "three") {
t2.firstChild.nodeValue = "two";
} else {
t2.firstChild.nodeValue = "three";
}
}
// add event listener to table
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);
onclick:
function initElement() {
var p = document.getElementById("foo");
// NOTE: showAlert(); or showAlert(param); will NOT work here.
// Must be a reference to a function name, not a function call.
p.onclick = showAlert;
};
function showAlert(event) {
alert("onclick Event detected!");
}
I guess Chris Baker pretty much summed it up in an excellent answer but I would like to add to that with addEventListener() you can also use options parameter which gives you more control over your events. For example - If you just want to run your event once then you can use { once: true } as an option parameter when adding your event to only call it once.
function greet() {
console.log("Hello");
}
document.querySelector("button").addEventListener('click', greet, { once: true })
The above function will only print "Hello" once.
Also, if you want to cleanup your events then there is also the option to removeEventListener(). Although there are advantages of using addEventListener() but you should still be careful if your targeting audience is using Internet Explorer then this method might not work in all situation. You can also read about addEventListener on MDN, they gave quite a good explanation on how to use them.
If you are not too worried about browser support, there is a way to rebind the 'this' reference in the function called by the event. It will normally point to the element that generated the event when the function is executed, which is not always what you want. The tricky part is to at the same time be able to remove the very same event listener, as shown in this example: http://jsfiddle.net/roenbaeck/vBYu3/
/*
Testing that the function returned from bind is rereferenceable,
such that it can be added and removed as an event listener.
*/
function MyImportantCalloutToYou(message, otherMessage) {
// the following is necessary as calling bind again does
// not return the same function, so instead we replace the
// original function with the one bound to this instance
this.swap = this.swap.bind(this);
this.element = document.createElement('div');
this.element.addEventListener('click', this.swap, false);
document.body.appendChild(this.element);
}
MyImportantCalloutToYou.prototype = {
element: null,
swap: function() {
// now this function can be properly removed
this.element.removeEventListener('click', this.swap, false);
}
}
The code above works well in Chrome, and there's probably some shim around making "bind" compatible with other browsers.
Using inline handlers is incompatible with Content Security Policy so the addEventListener approach is more secure from that point of view. Of course you can enable the inline handlers with unsafe-inline but, as the name suggests, it's not safe as it brings back the whole hordes of JavaScript exploits that CSP prevents.
It should also be possible to either extend the listener by prototyping it (if we have a reference to it and its not an anonymous function) -or make the onclick call a call to a function library (a function calling other functions).
Like:
elm.onclick = myFunctionList;
function myFunctionList(){
myFunc1();
myFunc2();
}
This means we never have to change the onclick call just alter the function myFunctionList() to do whatever we want, but this leaves us without control of bubbling/catching phases so should be avoided for newer browsers.
addEventListener lets you set multiple handlers, but isn't supported in IE8 or lower.
IE does have attachEvent, but it's not exactly the same.
The context referenced by 'this' keyword in JavasSript is different.
look at the following code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<input id="btnSubmit" type="button" value="Submit" />
<script>
function disable() {
this.disabled = true;
}
var btnSubmit = document.getElementById('btnSubmit');
btnSubmit.onclick = disable();
//btnSubmit.addEventListener('click', disable, false);
</script>
</body>
</html>
What it does is really simple. when you click the button, the button will be disabled automatically.
First when you try to hook up the events in this way button.onclick = function(),
onclick event will be triggered by clicking the button, however, the button will not be disabled because there's no explicit binding between button.onclick and onclick event handler. If you debug see the 'this' object, you can see it refers to 'window' object.
Secondly, if you comment btnSubmit.onclick = disable(); and uncomment
//btnSubmit.addEventListener('click', disable, false); you can see that the button is disabled because with this way there's explicit binding between button.onclick event and onclick event handler. If you debug into disable function, you can see 'this' refers to the button control rather than the window.
This is something I don't like about JavaScript which is inconsistency.
Btw, if you are using jQuery($('#btnSubmit').on('click', disable);), it uses explicit binding.
onclick is basically an addEventListener that specifically performs a function when the element is clicked. So, useful when you have a button that does simple operations, like a calculator button. addEventlistener can be used for a multitude of things like performing an operation when DOM or all content is loaded, akin to window.onload but with more control.
Note, You can actually use more than one event with inline, or at least by using onclick by seperating each function with a semi-colon, like this....
I wouldn't write a function with inline, as you could potentially have problems later and it would be messy imo. Just use it to call functions already done in your script file.
Which one you use I suppose would depend on what you want. addEventListener for complex operations and onclick for simple. I've seen some projects not attach a specific one to elements and would instead implement a more global eventlistener that would determine if a tap was on a button and perform certain tasks depending on what was pressed. Imo that could potentially lead to problems I'd think, and albeit small, probably, a resource waste if that eventlistener had to handle each and every click
in my Visual Studio Code, addEventListener has Real Intellisense on event
but onclick does not, only fake ones
let element = document.queryselector('id or classname');
element.addeventlistiner('click',()=>{
do work
})
<button onclick="click()">click</click>`
function click(){
do work
};

Can you destroy the document element and event listeners without using document.write()?

In the example below:
If you right click the document, it will tell you it's listening.
If you click m1 it will replace the document element, but right clicking the document will still inform you that it's listening. You must right click near the top because the document has no contents.
If you click m2 it will overwrite the document contents and right clicking near the top no-longer does anything. Examining the document in the development tools verifies that the event handlers are gone.
After pressing one button, you must "run code snippet" again to try the next because this demonstration is destructive.
With this information. Is there a different way to destroy the document and replace it with a new one, in such a way that the event handlers are destroyed, without using the document.write() function?
document.write() is prone to errors and its usage is "strongly discouraged", but I would still like to be able to destroy the document and it's event listeners.
document.addEventListener('contextmenu', function (e) {
alert('still listening');
e.preventDefault();
})
function m1() {
var doc = document.implementation.createHTMLDocument();
document.replaceChild(
document.importNode(doc.documentElement, true),
document.documentElement
);
}
function m2() {
document.close();
document.write('<html></html>');
}
<button onclick="m1()">m1</button>
<button onclick="m2()">m2</button>
To be clear, button/function "m1" fails my goals, because although the document element was replaced, the event handlers from the previous document element were preserved for some reason. I would like to achieve what m2 achieves, but without using document.write and document.close which are recommended against.
Addendum:
This question is strictly for the sake of better understanding the limits of the language and the engines that implement them. Please do not try to read between the lines or solve some alternate goal. It's just a question of whether something is possible or not.
I don't need to know how to remove event listeners or manage event listeners, or remove all child elements of a document. I would like to know if it's possible to destroy the document element itself, leaving no <html> tag whatsoever and leaving none of its event listeners behind. This is possible with document.write() but I simply want to know if there are alternate means of achieving this exact goal, not any other assumed goals.
I just realised that actually you can, and that the first example, m1, actually does replace the documentElement and removes its event listeners. The interesting thing is that there were no event listeners on it in the first place, they were on the document itself not the document element. The developer tools (in FireFox) tricks you and shows them as being attached to the <html> element, even though they aren't technically attached to an element. If you modify the example to actually attach the events to the document.documentElement instead of the document itself the event will be disabled when you click m1:
document.documentElement.addEventListener('contextmenu', function (e) {
alert('still listening');
e.preventDefault();
})
function m1() {
var doc = document.implementation.createHTMLDocument();
document.replaceChild(
document.importNode(doc.documentElement, true),
document.documentElement
);
}
function m2() {
document.close();
document.write('<html></html>');
}
<button onclick="m1()">m1</button>
<button onclick="m2()">m2</button>
Originally I was going to say no, and that you can't do it, as user Livingston Samuel states in this similar but not identical question: "You cannot replace the current document object or any document object with the Document object created with createHTMLDocument method."
Sticking to my original question though, it is not the document I was asking about, it was the documentElement. So given the modified code above, I will say that Yes, it is possible.
It is interesting to see that the event listeners are displayed on the html element in FireFox whether they are attached to the document object or the documentElement object, as of version 81. I might expect that they would instead not display them at all when attached to document, or put them on some abstract object. An iframe does have an abstract object available labeled #document, but it is not used for this and has no event label:
It is also interesting that document.write() actually destroys the events on the document object, not just the document.documentElement. It might be documented somewhere in here but I can't seem to find it.
After testing Chrome I noticed that the developer tools distinguishes between events being attached to the documentElement:
and the document itself:
I'm not sure what's behind this question but I may guess, that you are having issues when the objects and events are re-created in the page.
If this is the case, as can be seen in MDN (https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild), despite you remove the elements on the HTML, they still could remain in the memory for a while, waiting for the garbage collector to clean them up.
So in case you wish to remove safely a piece of code that contains events, the safest way is to remove the events first using your favorite library (like jQuery) or using the DOM through native JavaScript:
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
Once you have removed all events, the you could remove all elements, and the safest way to do this (from a memory point of view) is through a loop, removing the inner elements first and the parent elements then, and so on.
You can use document.getElementById('id-here').innerHTML = "", like so:
<html id="the-website">
<body>
<input type="button" onclick="document.getElementById('the-website').innerHTML = '';" value="Don't click me plz!">
</body>
</html>

How can I see the event that is attached to an html element?

I am a new programmer and still learning.
This is the code that I am trying to figure out:
<div id="buy" class="buy button">Buy</div>
When I click on the div (button), some JavaScript code is executed but I don't know were it is. How can I tell what function is fired when click happens? Some how a listener is attached to this element.
In Google chrome's developer tools (click the wrench icon >Tools>Developer tools), select the element in the Elements tab, on the right open the 'Event Listeners' panel you'll will see all events
If you use Firefox and Firebug you can try installing FireQuery. It will make it so you can see the handlers bound by jQuery. http://firequery.binaryage.com/
You can't do it in a really good manner by "just" using ECMAscript itself. For instance, if there was a click event handler added by DOM Level 1 in the form of
document.getElementById('buy').onclick = function() {};
you can of course easily intercept that property on the node itself. Things are getting more complicated if DOM Level 2 comes into play with .addEventListener() respectevily .attachEvent(). Now you don't really have a "place" to look for where all the different listener functions where bound from.
It gets better by using jQuery. jQuery will hold all it's event handler functions in a special object which is linked to the DOM node of invocation. You can check for that by getting the .data()-expando property for a node like
$('#buy').data('events');
However, now I already described three different ways of binding event listeners to a node (actually its two because a library like jQuery also uses DOM Level 1 or 2 methods of course).
It's really getting ugly if an event is triggerd by delegation. That means, we bound our click event on some parent-node just waiting for that event bubbling up to us so we can check the target. So now we don't even have a direct relationship between the node and the event listener.
Conclusion here is, lookout of a browser plugin or probably a thing like VisualEvent.
You may use "Visual Event 2" script as a bookmark or same script as Chrome extension.
This script shows all js events attached to dom-elements.
Use jQuery("#buy").data('events');
http://api.jquery.com/jQuery.data/ may be interesting.
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.
If you're using FireFox, you should have FireBug installed. Once you have that, you can install FireQuery, which will show you what jQuery events are bound to which objects.
http://getfirebug.com/
http://firequery.binaryage.com/
This is the easiest way I've found of how to do it:
http://www.sprymedia.co.uk/article/Visual+Event
When working with events in Javascript, it is often easy to lose track
of what events are subscribed where. This is particularly true if you
are using a large number of events, which is typical in a modern
interface employing progressive enhancement. Javascript libraries also
add another degree of complexity to listeners from a technical point
of view, while from a developers point of view they of course can make
life much easier! But when things go wrong it can be difficult to
trace down why this might be.
It is due to this I've put together a Javascript bookmarklet called
Visual Event which visually shows the elements on a page that have
events subscribed to them, what those events are and the function that
the event would run when triggered. This is primarily intended to
assist debugging, but it can also be very interesting and informative
to see the subscribed events on other pages.
There's a bookmark button there you can drag to your toolbar (FF or Chrome), then just click the button on any page where you want to see the events attached. Works great! (at least for events attached by jQuery or other libraries).
Are you using jQuery? If so, you want to search for one of these three lines of code:
$("#buy").click //the div is refered by its id
or
$(".buy").click //the div is refered to by the style "buy"
or
$(".button").click //refered to by the style "button"
Most newer browsers have "Developer Tools" built into them by pressing F12 (at least in IE and Chrome). That may help you do some further debugging and tracing.
Below is something I’ve used in the past that I think may be what you're looking for. What this does is watch a property on a page element (In the example below, it's the document's "Title" property) and then display an alert with the JS callstack whenever that property is changed. You’ll need to get this into the DOM before whatever code you're trying to find fires, but hopefully you’ll be able to identify what’s causing the problem.
I would recommend using Firefox and getting Firebug for JavaScript debugging.
// Call stack code
function showCallStack() {
var f=showCallStack,result="Call stack:\n";
while((f=f.caller)!==null) {
var sFunctionName = f.toString().match(/^function (\w+)\(/)
sFunctionName = (sFunctionName) ? sFunctionName[1] : 'anonymous function';
result += sFunctionName;
result += getArguments(f.toString(), f.arguments);
result += "\n";
}
alert(result);
}
function getArguments(sFunction, a) {
var i = sFunction.indexOf(' ');
var ii = sFunction.indexOf('(');
var iii = sFunction.indexOf(')');
var aArgs = sFunction.substr(ii+1, iii-ii-1).split(',')
var sArgs = '';
for(var i=0; i<a.length; i++) {
var q = ('string' == typeof a[i]) ? '"' : '';
sArgs+=((i>0) ? ', ' : '')+(typeof a[i])+' '+aArgs[i]+':'+q+a[i]+q+'';
}
return '('+sArgs+')';
}
var watchTitle = function(id, oldval, newval) { showCallStack(); }
// !! This is all you should need to update, setting it to whatever you want to watch.
document.watch("title", watchTitle);
Right-click page, and choose to view the page's source
Find <script> tags
Look for $("#buy") and something mentioning onClick or .on("click",function(){...});
If you can't find it, search for something along these lines: document.getElementById("buy")
You have found the function, or code, where the event handler code is
$("#buy") is JQuery's way of saying find an element that has an id attribute of buy and if it has a . following it with some function, that function is acting upon the element that was found by JQuery.

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

How to debug JavaScript / jQuery event bindings with Firebug or similar tools?

I need to debug a web application that uses jQuery to do some fairly complex and messy DOM manipulation. At one point, some of the events that were bound to particular elements, are not fired and simply stop working.
If I had a capability to edit the application source, I would drill down and add a bunch of Firebug console.log() statements and comment/uncomment pieces of code to try to pinpoint the problem. But let's assume I cannot edit the application code and need to work entirely in Firefox using Firebug or similar tools.
Firebug is very good at letting me navigate and manipulate the DOM. So far, though, I have not been able to figure out how to do event debugging with Firebug. Specifically, I just want to see a list of event handlers bound to a particular element at a given time (using Firebug JavaScript breakpoints to trace the changes). But either Firebug does not have the capability to see bound events, or I'm too dumb to find it. :-)
Any recommendations or ideas? Ideally, I would just like to see and edit events bound to elements, similarly to how I can edit DOM today.
See How to find event listeners on a DOM node.
In a nutshell, assuming at some point an event handler is attached to your element (eg): $('#foo').click(function() { console.log('clicked!') });
You inspect it like so:
jQuery 1.3.x
var clickEvents = $('#foo').data("events").click;
jQuery.each(clickEvents, function(key, value) {
console.log(value) // prints "function() { console.log('clicked!') }"
})
jQuery 1.4.x
var clickEvents = $('#foo').data("events").click;
jQuery.each(clickEvents, function(key, handlerObj) {
console.log(handlerObj.handler) // prints "function() { console.log('clicked!') }"
})
See jQuery.fn.data (where jQuery stores your handler internally).
jQuery 1.8.x
var clickEvents = $._data($('#foo')[0], "events").click;
jQuery.each(clickEvents, function(key, handlerObj) {
console.log(handlerObj.handler) // prints "function() { console.log('clicked!') }"
})
There's a nice bookmarklet called Visual Event that can show you all the events attached to an element. It has color-coded highlights for different types of events (mouse, keyboard, etc.). When you hover over them, it shows the body of the event handler, how it was attached, and the file/line number (on WebKit and Opera). You can also trigger the event manually.
It can't find every event because there's no standard way to look up what event handlers are attached to an element, but it works with popular libraries like jQuery, Prototype, MooTools, YUI, etc.
You could use FireQuery. It shows any events attached to DOM elements in the Firebug's HTML tab. It also shows any data attached to the elements through $.data.
Here's a plugin which can list all event handlers for any given element/event:
$.fn.listHandlers = function(events, outputFunction) {
return this.each(function(i){
var elem = this,
dEvents = $(this).data('events');
if (!dEvents) {return;}
$.each(dEvents, function(name, handler){
if((new RegExp('^(' + (events === '*' ? '.+' : events.replace(',','|').replace(/^on/i,'')) + ')$' ,'i')).test(name)) {
$.each(handler, function(i,handler){
outputFunction(elem, '\n' + i + ': [' + name + '] : ' + handler );
});
}
});
});
};
Use it like this:
// List all onclick handlers of all anchor elements:
$('a').listHandlers('onclick', console.info);
// List all handlers for all events of all elements:
$('*').listHandlers('*', console.info);
// Write a custom output function:
$('#whatever').listHandlers('click',function(element,data){
$('body').prepend('<br />' + element.nodeName + ': <br /><pre>' + data + '<\/pre>');
});
Src: (my blog) -> http://james.padolsey.com/javascript/debug-jquery-events-with-listhandlers/
The WebKit Developer Console (found in Chrome, Safari, etc.) lets you view attached events for elements.
More detail in this Stack Overflow question
Use $._data(htmlElement, "events") in jquery 1.7+;
ex:
$._data(document, "events") or $._data($('.class_name').get(0), "events")
As a colleague suggested, console.log > alert:
var clickEvents = $('#foo').data("events").click;
jQuery.each(clickEvents, function(key, value) {
console.log(value);
})
jQuery stores events in the following:
$("a#somefoo").data("events")
Doing a console.log($("a#somefoo").data("events")) should list the events attached to that element.
Using DevTools in the latest Chrome (v29) I find these two tips very helpful for debugging events:
Listing jQuery events of the last selected DOM element
Inspect an element on the page
type the following in the console:
$._data($0, "events") //assuming jQuery 1.7+
It will list all jQuery event objects associated with it, expand the interested event, right-click on the function of the "handler" property and choose "Show function definition". It will open the file containing the specified function.
Utilizing the monitorEvents() command
ev icon next to elements
Within the Firefox Developer Tools' Inspector panel lists all events bound to an element.
First select an element with Ctrl + Shift + C, e.g. Stack Overflow's upvote arrow.
Click on the ev icon to the right of the element, and a dialogue opens:
Click on the pause sign || symbol for the event you want, and this opens the debugger on the line of the handler.
You can now place a breakpoint there as usual in the debugger, by clicking on the left margin of the line.
This is mentioned at: https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_event_listeners
Unfortunately, I couldn't find a way for this to play nicely with prettyfication, it just seems to open at the minified line: How to beautify Javascript and CSS in Firefox / Firebug?
Tested on Firefox 42.
Looks like FireBug crew is working on an EventBug extension. It will add another panel to FireBug - Events.
"The events panel will list all of the event handlers on the page grouped by event type. For each event type you can open up to see the elements the listeners are bound to and summary of the function source." EventBug Rising
Although they cannot say right now when it will be released.
I also found jQuery Debugger in the chrome store. You can click on a dom item and it will show all events bound to it along with the callback function. I was debugging an application where events weren't being removed properly and this helped me track it down in minutes. Obviously this is for chrome though, not firefox.
According to this thread, there is no way in Firebug to view what events are attached to listeners on a DOM element.
It looks like the best you can do is either what tj111 suggests, or you could right-click the element in the HTML viewer, and click "Log Events" so you can see which events are firing for a particular DOM element. I suppose one could do that to see what events could be firing off particular functions.
With version 2.0 Firebug introduced an Events panel, which lists all events for the element currently selected within the HTML panel.
It can also display event listeners wrapped into jQuery event bindings in case the option Show Wrapped Listeners is checked, which you can reach via the Events panel's options menu.
With that panel the workflow to debug an event handler is as follows:
Select the element with the event listener you want to debug
Inside the Events side panel right-click the function under the related event and choose Set Breakpoint
Trigger the event
=> The script execution will stop at the first line of the event handler function and you can step debug it.
Firebug 2 does now incorporate DOM events debugging / inspection.

Categories

Resources