January 6, 2009 17

Announcing jQuery.Firebug

By in firebug, javascript, jquery

I have been sitting on my latest jQuery plugin for some time now. Although I realize that the code is not yet of production quality and there are certainly bugs and features that remain to be addressed, I’ve decided that I should at least release this plugin to the wild. At the very least, I would love some feedback on it and possibly new features to be added. “So let’s see it!” you ask?

jQuery.Firebug is a jQuery plugin that simply exposes the Firebug Console API to the jQuery object. That’s about it. Under the covers, it bridges some functionality between Firebug and Firebug Lite and has a host of other small feature. But all in all, it simply adds the Console API methods directly to the jQuery object.

The goal of this plugin is to allow inspection of your jQuery selections while in the middle of a chain. For those of you who have ever had a jQuery chain like:

$(".elements").parents("div")
.find(".new").show().end()
.find(".old").hide();

and you load up the page and it doesn’t work. How do you begin debugging? You open up Firebug but are unable to easily ‘step through’ the jQuery chain. Inevitably, you have to break up each selector, assign it to a temporary variable solely to call console.log(temp) on your selection. Enter jQuery.Firebug:

$(".elements").log()
.parents("div").log()
.find(".new").log()
.show().end().log()
.find(".old").log()
.hide();

Each log method returns the same selection that was passed to it, so you can simply continue your chain as if it weren’t even there. Every Firebug method (as of Firebug 1.2) is supported so you can call debug(), assert(), info(), dir(), profile(), etc.

There are a few additional features that I will address later as the code begins to settle down. For now, the source and documentation can be found in Subversion at svn.jasonkarns.com/jquery/firebug.  There is much work to be done on the plugin as well as on the documentation. Until then, let me hear any feedback you may have.

 
 

Tags: , , ,

17 Responses to “Announcing jQuery.Firebug”

  1. Wicked! I will put this into practice and let you know how it works out.

    Cheers.

    Joe

  2. uwalter says:

    That’s good news, indeed!

    Was redirected to your blog by jQuery itself. :)

    Will checkout your stuff and do some testing. Where to post notes, bugs or fixes?

    Best Regards
    Uwe

  3. […] include a jQuery chain can be annoying at best and virtually impossible at worst. Now there is a jQuery Firebug Plugin that helps when debugging Javascript that uses jQuery and it seems to do it very well! The plugin […]

  4. Sweet! Trying it out now.

  5. Mauvis says:

    Great concept. I started working on the same thing after I saw something similar on snipplr: http://snipplr.com/view/10358/jquery-to-firebug-logging/

    My two primary complaints about his method (which you solved the first) are:
    1) It didn’t actually bring all / most of the Firebug functionality to jQuery – it just supplied a “hook” to call it (while requiring an initial argument (the firebug method you want) which I wasn’t a big fan of.
    2) This is the most important part: when calling Firebug within jQuery the this keyword should always point to the current jQuery wrapper. This is how all jQuery internal methods and all jQuery plugins function. I tried in earnest to call the Firebug methods in the context of he jQuery object (using Func.call or Func.apply) but I think the Firebug object is system-level and it’s methods always point to it’s own object.

    I could add in a check to see if the this keyword is passed and change the reference to the jQuery object if it’s the Firebug one but this seems rather hacky – still if it’s the only solution anyone can think of it should do the trick.

    Let me know if you’re looking for any collaborators. You could also throw this up on GitHub and see the colorful amount of variants that comes of it – some of the strongest concepts could be merged back into the master trunk.

  6. jasonkarns says:

    @Mauvis
    I’m not sure if I follow your second issue. You’re saying you want ‘this‘ to point to the current jQuery wrapper from inside which function? Using log() as an example, if I call $(".elements").log() then ‘this‘ in the context of the log method refers to the jQuery selection. But inside this function (and most of the other similar functions like debug, info, error, etc), the following occurs (pseudo-code):

    console.log(this); //refers to jQuery selection
    console.group();
    //foreach selected element
    foreach(x in this){
    console.log(x);
    }
    console.groupEnd();
    

    So for each console command executed on a jQuery selection, you actually end up with 2 output statements in the console. One for the jQuery selection itself (and any parameters you pass to the command). Then a set of statements, one for each item in the jQuery selection, all wrapped in a ‘foldable’ group.

    I don’t see why a user of this plugin would need access to ‘this‘ inside of this functions. Do you plan on adding extra functionality inside the console methods that are attached to the jQuery object or to the console methods attached to the console object? Can you give me an example of what you are trying to do?

  7. Mauvis says:

    Hi Jason,

    Since all methods in the jQuery object belong to the jQuery object the this keyword referenced in those function should always be the jQuery object. For example:

    $("script").filter(function(){
    	return this.src && this.src.toLowerCase().indexOf('jquery') !== -1;   
    })
    

    Notice in the anonymous function passed to the jQuery .filter method above, this always references the current element in the matched set. Every time it’s called it moves to the next matched element until it’s done (kind of like passing a block to an each method in Ruby). Whenever you use the this keyword in a jQuery method it either references the whole match set, or conveniently the current pointer element in a loop.

    Your method is good but from a user perspective it assumes that the user wants to see every element in the jQuery wrapper each time .log is called with anything.

    Take for example this example:

    $('*')
        .log('grabbing the dom')
        .filter(function(i){
            return this.src && this.src.toLowerCase().indexOf('jquery') !== -1    
        })
        .log('filter for jQuery scripts')
        .end()
        .log('reverting back to old object for more manipulation');
    

    The above method is obviously very contrived, but you can see how in a complex script you might get way more output then you wanted.

    If the this keyword were supported correctly, the above code might look like:

    $('*')
        .log('grabbing the dom, %s elements', this.length) // or more simply
        .log('grabbing the dom ', this.length, ' elements') 
        .filter(function(i){
            return this.src && this.src.toLowerCase().indexOf('jquery') !== -1    
        })
        .log('filter for jQuery scripts: ', this) // this would log the current jQ wrapped set 
        .log('first script is: ', this[0].src) // easily logging only the relevant information
        .end()
        .log('reverting back to old object for more manipulation');
    

    The only console output I would get above would be from whatever I specified in my logging. Because I get to cherry-pick the details I want there would be no need to echo out the full matched set every time I logged something.

    While there is a definitely a use for your current iteration of .log, perhaps it should be placed in an extra .jlog method or only trigger when you .log with no arguments and .log should work exactly like it does in Firebug with the exception being the jQuery-like this behavior.

    Best,

    Mauvis

  8. jasonkarns says:

    @Mauvis
    Thanks for the comment, I see what you are getting at now. I will reply in two parts.
    First, I agree with your general comment that in most cases, the user should be able to decide how much is output. I will have to think of a way to do this. Perhaps parameter passing is the answer, perhaps additional methods. Either way, there is definitely some room for improvement here.

    However, to argue the second point — being the passing of this to the log methods (or any of the methods) — your syntax is flawed. When you use the keyword this as a parameter to a function, the object that you pass in has already been de-referenced upon entry to the method. In your example, $(*).log(this), this refers to the context object at the point of invocation.

    
    $("a").click(onClickHandler);
    function onClickHandler(e){
      var x = this;
      var y = $(this).get(0);
      console.log(this, x, y);// you will get a log statement with three identical objects
      $(*).log(this, x, y);// you will get a log statement with three identical objects, 
        //followed by the grouped log statements foreach element in $(*)
    }
    
    

    What you have to remember is that in the log method that I’ve defined on the jQuery object (and in fact, any JavaScript function at all), when you pass this as a parameter to a function, what the function gets is just a regular object. There is nothing special about this when used as a parameter. It is indistinguishable from x and y as used above. Therefore, there is no way to ‘change’ what this points to.

    However, there is a way to hack it in. Currently, if you pass (as a string) to any of the log functions anything beginning with a dot, then jQuery.Firebug will assume you are attempting to reference a property or method on the jQuery object. For instance:

    $(*).log(".length", ".attr('id')");

    This statement would output the actual length of the jQuery selection as an integer, followed by the ID attribute of the first element of the jQuery selection, followed by the grouped output of each element in the jQuery selection. I believe this is exactly the kind of behavior you wanted. In order to add support for this as well, I would have to declare a special string parameter such as “this” or “[this]” or something similar.

  9. Mauvis says:

    Jason,

    You’re right, my logic was flawed there a bit.

    Passing this to a function in the global scope would just pass in a reference to the document object. Foolish me. :)

    Glad you could see the practicality of what I was trying to do, though.

    Best,

    Mauvis

  10. h3 says:

    Great idea, very promising.

  11. beppu says:

    I see that you made it portable across browsers. Very good work. This will be extremely useful for debugging.

  12. tsdragon says:

    Wonderful! But how is it installed? How is it used? What part of the svn is the useful part? In the trunk there’s three different .js files for some aspect of firebug and in the firebuglite subdirectory there’s another two. How am I supposed to know which is which?

  13. jasonkarns says:

    Stay tuned, I’m in the process of moving my code repos to GitHub and I’ll be doing a cleanup of the code in the process.

  14. […] a result of some of the discussion following from my post on my new jQuery plugin, jQuery.Firebug I’m soliciting feedback for its desired behavior. […]

  15. Todd says:

    Hey Jason–

    I just grabbed the plugin from SVN. Cool stuff, but I had a problem. Your line 338 is the following:

    window.console[“_” + method]($.makeArray(arguments).join(” “));

    This is concatenating all objects as strings, which results in a much less useful output in Firebug (e.g. no drilling on object properties). Furthermore, it nerfs all the standard Firebug output in the same way, since you’re redefining the console.* functions to do the same.

    Instead, I changed it to the following and had good results:

    window.console[“_” + method].apply(this,arguments);

    After this, Firebug works as expected. Thanks!

  16. shreyash says:

    if i doesn’t fill any field in your leave a reply boxes then it will gone to window which has no any way to go back to this window

  17. Jesse says:

    Looks interesting, but I can’t find a link to download the plugin anywhere. Didn’t see it on your github. How do we get jQuery.Firebug?

Leave a Reply

You must be logged in to post a comment.