6

I'm having problems with debugging DOM changes introduced by some JavaScript code I'm running. Somewhere in the code an element's class is changed, and I'm trying to pinpoint where exactly. Unfortunately, the new class name is so generic that searching through all the JS code gives too many results to be a viable option.

I've tried debugging a bit with Firebug, but despite the nice "Break on Attribute Change" feature, I can't get it to work in a way I would want. The Firebug demo works correctly, but it's a post load situation.

The problem seems to be that I want to watch for mutations before the page is fully loaded. I assume that the changes occur somewhere in $(document).ready(), so it's in the DOM, but I can't select elements for UI breakpoints as would be the case with the demo (after page load).

Is there some way to debug this kind of situation other than grepping/going through the code by hand?

Karol J. Piczak
  • 585
  • 1
  • 5
  • 24
  • 2
    Use the script tab and set breakpoints. If you're using a mess of closures with jQuery, you may want to explore a module approach so you can accurately describe your code and detect gets or sets. – rxgx Apr 30 '11 at 13:36
  • 1
    I'm afraid manually setting up code breakpoints is no better than logically browsing through the whole codebase. What I hoped for is applying *Break on Attribute Change* functionality after the DOM is ready, but before a full page load occurs. But I have yet to find a way to achieve it in *Firebug*. Even if I manually break in `$(document).ready()` I'm not able to freely inspect the document. I don't know if it's a limitation/bug/by design. After a complete page load further debugging works fine, but it's water over the dam, the changes are already in place. – Karol J. Piczak Apr 30 '11 at 16:26
  • 1
    And the whole issue is bearable if you more or less know the codebase, but diving into open source projects you have no experience with makes for a painfully slow debugging sessions. So that's why I'm looking for a way to speed things up a bit. – Karol J. Piczak Apr 30 '11 at 16:28
  • 1
    Thank you for all your answers, it was hard to choose whom to award the bounty, but in the end I decided *@jsgoupil*'s answer was first and helpful. For more complex non-jQuery mutations I would probably choose some other tricks presented here. Thanks! – Karol J. Piczak May 06 '11 at 10:06

4 Answers4

4

I propose that you remove the target element where its classname is changed. With some luck, you may be able to generate an error in the JavaScript code, so you will find where is your problem.

Otherwise, if it's done by JQuery (addClass), you may want to modify the JQuery code itself just to figure out the callstack.

The code would look like this (make sure this code is the first code called after JQuery inclusion):

(function () {
    var addClass = jQuery.fn.addClass;
    jQuery.fn.addClass = function (value) {
        for (var i = 0, l = this.length; i < l; i++) {
            // Here you put your method to start the debugger if you get your right element
            if (this[i].id === "abc") {
                debugger;
            }
        }
        addClass(value);
    }
})();

Hope that helps.

jsgoupil
  • 3,788
  • 3
  • 38
  • 53
  • Hey, thanks. The second part, though specific to *jQuery*, is probably the most useful hint so far. – Karol J. Piczak May 03 '11 at 17:21
  • @Karol I don't know if you fixed your issue by now, but have you considered to try with Chrome? They also have "Break on *" stuff with their DevTool. – jsgoupil Jun 28 '11 at 22:24
  • Yes, I did fix it a long time ago. Just had to spent some more hours on this. Unfortunately, in my case Chrome behaves the same way as Firefox. It won't capture mutations occurring before page load. – Karol J. Piczak Jun 29 '11 at 09:02
1

This answer may sound pretty lame, but I honestly think the best solution for bugs like this is "deconstructing" your program. Just make a copy of the entire project, then rip it apart. Take out chunks of code one by one. Turn function calls into stub functions or whatever to keep things running. Find the minimal amount of code that triggers the bug. Then the solution should be obvious.

Jesse Aldridge
  • 7,991
  • 9
  • 48
  • 75
  • That's mostly what I'm doing. Banging my head, stepping through the code, trying to pinpoint the issue however I can. Mostly (in fact I think all the time) I get to the place in the end. But sometimes it takes half a day for an issue I feel could be 2-clicks away. That's what's so painful in this. And I know I have all the info under the hood, but I just can't use it (page is not yet ready, but the server generated *DOM* is, so I should be able to just listen to further mutations on client-side). – Karol J. Piczak May 03 '11 at 17:19
1

Have you considered adding a mutation event? The event I think you want, DOMAttrModified, is not supported in webkit, so you might have to test with Firefox or Opera. In fact it is deprecated in DOM level 3.

There are two jQuery plugins for mutation events here (documentation) and here but since you want to do this before page load they might not be the answer.

You are probably best writing your own JavaScript bind for this event - there is an example in the answer to is there an alternative to DOMAttrModified that will work in webkit

I hope this helps.

Community
  • 1
  • 1
andyb
  • 43,435
  • 12
  • 121
  • 150
1

If you want to use the "Break on Attribute Change" feature to debug, you can do the following:

  1. Comment out all JS in the page (which is hopefully all in the head) except for the base jQuery load.

  2. Load the page and set your watch points.

  3. Add a button, which fires the JS, to the HTML. Or, optionally fire it from the console.

  4. Trigger the JS load/fire. Hopefully your watch and break points will fire as desired.

For example, suppose your page loads/has:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript"></script>
<script src="/Library_X.js" type="text/javascript"></script>
<script src="/MyJS.js" type="text/javascript"></script>

Then you could run this code in the console after setting the watch points:

function addJS_Node (text, s_URL)
{
    var scriptNode                      = document.createElement ('script');
    scriptNode.type                     = "text/javascript";

    if (text)  scriptNode.textContent   = text;
    if (s_URL) scriptNode.src           = s_URL;

    document.head.appendChild (scriptNode);
}

addJS_Node (null, '/Library_X.js');
//-- Possible pause here.
addJS_Node (null, '/MyJS.js');
// etc.

Or temporarily code a button that fires the same JS, into the page's HTML.

Brock Adams
  • 90,639
  • 22
  • 233
  • 295