DataTables / VisualEvent

Visually show Javascript events on a page
http://sprymedia.co.uk
1.04k stars 162 forks source link

If possible: Who did the binding of the event? #31

Open netsi1964 opened 10 years ago

netsi1964 commented 10 years ago

This is a feature request.

While debugging events it is so cool the stuff which VisualEvent does in the current version, however to make it even more cool this could be added:

Tracking of the binding process, that is: I could see in VisualEvent which code (where with link) did the binding of the event. "Who is watching?" - so that can backtrack events to the part of the code which has added the event. An example could be:

function clickHandler() {
  // I'm the handler
}

$(function() {
  // Im am binding the handler to elements
 $('li').on('click', clickHandler);
});```

I do not know if that is possible, but hey it would be a great feature! :-)

/Sten
DataTables commented 10 years ago

Agreed! However, when I looked at this previously, I couldn't find a way to do it - at least not in all browsers. The Function.prototype.toString() in Webkit returns the function as it was originally formatted, including white-space, so what I've done in Webkit browsers is to have it Ajax load, as text, the Javascript source files, and it will then look over those files for the function and attempt to display where it was sourced from.

Firefox, IE etc reflow the function, so its not possible in those browsers. I've also found it isn't 100% reliable in webkit browsers, but haven't looked into why that is yet. I'll leave this bug open to see if I can pin point that.

ticky commented 10 years ago

I think what @netsi1964 means is that it would be cool if, for the example above, it could show not only the function defined on line 1, but also the binding context of line 7.

DataTables commented 10 years ago

It will show where in the code it was defined, so you would be able to see the context, but you couldn't be able to "Inspect" it like you can on the console.

ticky commented 10 years ago

Yes, but it only shows the function definition, not the binding context, which is sometimes different - the definition of ClickHandler could be somewhere else in the file (or in another file altogether), while it is bound by that jQuery on atline 7.

DataTables commented 10 years ago

My point is that where Visual Event says "Function definition could not be found automatically" (currently - not sure why it can't be found!) it should say, "Function definition in {file} line {#}.". Is that not what you are looking for?

As I say, that should work in Webkit browser (it used to!), but appears to be broken at the moment.

ticky commented 10 years ago

Yes, but I believe what @netsi1964 is after is in addition to that - which is very useful in itself.

i.e. for the above, it could output both the function definition and where it was bound to the element, like (for example) this;

Click event subscribed by jQuery 2.0 on line 7;

 $('li').on('click', clickHandler);

Handler function definition;

function clickHandler() {
  // I'm the handler
}
DataTables commented 10 years ago

Ah I see - no that's not possible without evaluating the Javascript. I can say where clickHandler was defined, but not where it was used or bound. I could show the lines around where it defined, but there is no way to say where it was applied.

ticky commented 10 years ago

It may be possible to combine this technique for retrieving a stack trace with overriding the built-in addEventListener to add this kind of functionality to the browser extension version of Visual Event.

A quick demo, which Works-For-Me™ in Chrome (YMMV), is as follows;

HTMLDocument.prototype._addEventListener = HTMLDocument.prototype.addEventListener;
Element.prototype._addEventListener = Element.prototype.addEventListener;
window._addEventListener = window.addEventListener;
HTMLDocument.prototype.addEventListener = function(a, b, c) {
    HTMLDocument.prototype._addEventListener.call(this, a, b, c);
    try {
        // Forces an error to be thrown
        non.existant.item+=0;
    } catch (e) {
        // Prints out the error (works in Chrome - differs in other browsers)
        console.log(e.stack);
    }
}
Element.prototype.addEventListener = function(a, b, c) {
    Element.prototype._addEventListener.call(this, a, b, c);
    try {
        non.existant.item+=0;
    } catch (e) {
        console.log(e.stack);
    }
}
window.addEventListener = function(a, b, c) {
    window._addEventListener.call(this, a, b, c);
    try {
        non.existant.item+=0;
    } catch (e) {
        console.log(e.stack);
    }
}

This, run before any other JS in the page, will print out the stack trace whenever addEventListener is called (whether from jQuery or other sources), as well as doing the default action.

From this, you get something like this;

ReferenceError: non is not defined
    at HTMLAnchorElement.Element.addEventListener (http://localhost/:22:6)
    at Object.v.event.add (http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js:2:34395)
    at HTMLAnchorElement.<anonymous> (http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js:2:43530)
    at Function.v.extend.each (http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js:2:14543)
    at v.fn.v.each (http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js:2:11217)
    at v.fn.extend.on (http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js:2:43506)
    at http://localhost/:79:15 

Which could then be parsed to work out where the event is attached (by looking for the next item in the stack after a jQuery "on", for instance).

This would not be possible from the bookmarklet, as it would have to be injected before any events were bound, so it may be out of scope for this project, but it would be useful context to have alongside VE.

DataTables commented 10 years ago

overriding the built-in addEventListener

That only works if I can replace the function before it is called. However, VisualEvent is run after the events have been attached. Perhaps VisualEvent could offer a "debug mode" script, which you would pre-load on your page to get extra information about your events, using this method... Not as handy, but might be useful.

ticky commented 10 years ago

This would not be possible from the bookmarklet, as it would have to be injected before any events were bound, so it may be out of scope for this project, but it would be useful context to have alongside VE.