The Javascript Console

Most web developers are familiar with some of the available developer tools out there in various browsers at this point. However, most people don’t use them anywhere near their full potential. The goal of this post is to explore the javascript console in a bit of detail, describing some of the features and techniques of using it, but without duplicating effort on the part of some other good blog posts. I’m sure I’ll miss some items that people deem appropriate or necessary, so please feel free to fill in the gaps in the comments below. I’ll mostly be speaking from the context of the Chrome developer tools, unless I specify otherwise, as I find them to be one of the more (if not the most) robust offerings currently.

This is the first in a series of posts about developer tools and debugging techniques, and is going to deal with the Console tab in developer tools. It’s probably one of the most commonly used tools, and offers you the ability to playtest various javascript expressions, either standalone or in the context of specific functions via breakpoints, which I’ll cover in another post. Chrome’s console has a lot of features that most people don’t know about, or don’t know how or why to use. I’ll try to dispel some of the mysticism in this post, and point out some of the more useful features available.

What is the console, and where do you find it? In some browsers, you can hit F12 on your keyboard to open up your developer tools. In others, you can try Cmd+Alt+J (Mac) or Ctrl+Shift+J (Windows). If none of those methods work, you can always try right-clicking any spot on the page and selecting “Inspect Element”, which should open your developer tools. Then just find the Console tab.

Chrome

Safari

IE9

Firefox

Firebug

The console itself is a place that you can use to execute arbitrary javascript statements, either single- or multi-line. Any valid javascript can be executed here, and its result displayed for inspection, but there are some console-specific functions that can also be used here. The support for these functions varies from browser to browser, and you can find a pretty good matrix of supported functions at this blog post about the Javascript console API. You’ll also find some good descriptions and examples in that post for what each of those console functions do. Another great resource on this subject is Brian Arnold‘s 2013 jQuery Conference presentation, “Digging, Debugging“, which covers some of this stuff at a high level, and has some good resource links of its own.

So what less-obvious things can we do from the console? For one thing, we can inspect and evaluate statements in our code in a live view, meaning if we set breakpoints to pause in the middle of various functions, we can execute statements in that function’s scope, with access to any variables in the current scope resolution chain. If we have the following example:

function foo () {
    var myVar = true;

    function bar () {
        var baz = false;

        console.log('Inside "bar" function');
    }

    bar();
}

We could set a breakpoint on our console.log() statement, and from the console tab, have access to inspect our ‘baz’ and ‘myVar’ variables, ‘this’, basically anything that the current scope chain has to offer for the current execution context. We could modify those variables as we saw fit (except ‘this’, of course), and we could execute one-off statements like this:

console.log('myVar is ' + (myVar === true ? 'true' : 'false'));

If one of our variables was an object or array, we could output its value to the console, and expand that value to inspect its contents. We could also alter those properties or array indexes however we wanted. If we had a variable that was a reference to an actual DOM node in the document, depending on our browser, we could even click on it (or right-click and select to display in the Elements panel) and be jumped directly to that node in our DOM inspector.

All of this logging of various data to the console can get to be a mess, though. If you want to make it more readable, you might consider grouping related log entries under a heading, which is when the console.group(), console.groupCollapsed(), and console.groupEnd() functions come in handy. The console.group() and console.groupCollapsed() functions each take N number of parameters to set a label for them, meaning you could do something like this:

function foo () {
    console.groupCollapsed('function foo');

    var bar = someOtherFunctionCall(),
        baz = $('#someElement');

    console.log('bar: ', bar);
    console.log('baz childNodes: ', baz.children().length);

    console.groupEnd();
}

The above code would give us 2 log messages that would be grouped under a heading of “function foo” in our console, which could be expanded to inspect, and then collapsed again to save room in the console. You can also group console warnings and errors in the same way as logged messages are grouped.

Now we come to the black magic and sorcery part of the console section, the $ identifiers. If you’re familiar with regular expression backreferences, this will seem pretty straightforward. If you’re not, all you really need to know is that $0 through $4 hold an array of entries in a LIFO format. Basically, as you inspect elements in your DOM inspector, the currently selected element becomes $0. When you inspect another element, $0 shifts to $1, and the current element becomes $0. This continues as you inspect more and more elements, bumping each one down the chain (up in number) until $4 gets bumped off the stack altogether, and $3 becomes $4. At any given time, you can simply enter $0 through $4 on the command line of your console, and inspect the element it represents. You can also use this form of shortcut in conjunction with whatever library/framework you happen to be running in your page. Say you wanted to attach a click event to your body tag in jQuery, and you currently have it selected in your DOM inspector. You could simply do this:

$($0).on('click', myHandler);

This would take the currently-selected item in your Elements panel, in this example, the body tag, and attach a click event listener to it. This particular scenario isn’t very useful, but demonstrates what it’s possible for you to do. Typically, you’re going to be inspecting elements you’re actually interested in as part of your debugging process, so it’s helpful to be able to just $0 through $4 your way through the list of your most recent selections, in case you need to inspect changing values or child nodes related to them.

There’s also $_, which will show you your most recently evaluated statement in the javascript console, whether it was an element, a function call, a variable, or anything else. Enter anything in the console, watch its console output, and then type $_ and hit enter, and watch the whole thing replay. Why is this useful? For a reason that the $0 through $4 references can be handy. The console will typically autosuggest additional object members, when you’re typing an actual object reference. The problem is that some of those object references are returned from function calls, and the console doesn’t evaluate those return values until after the call is actually invoked. That means that this:

// Will autosuggest 'childNodes' to complete this property name
> var blah = document.getElementById('blah');
> blah.childN

Whereas this:

// Won't autosuggest anything
> document.getElementByid('blah').childN

The console can’t evaluate the results of the call to the document.getElementById() function until it’s been made, so it has no idea how to autosuggest a continuation in that reference chain. However, using these $ references gives us object references for it to use after the fact. This means that we can now do something like this:

function foo () {
    return {
        foo: true,
        bar: true,
        baz: false
    };
}
> foo()
  Object {foo: true, bar: true, baz: false}
> $_.b // This will now autocomplete with 'bar' and 'baz' as options

This makes it much easier to navigate the reference paths on dynamically-generated objects that may be returned as the result of a function, and explore their members as we type in the console.

Another function you have available from the Chrome console command line is $$(), which will give you a shortcut to the native document.querySelectorAll() function, returning everything that matches a given CSS selector (and then allowing you to work with it in the $_ context).

> $$('input') // Returns an array of all input elements in the document

The $x() function will allow you to specify an xpath expression (starting from the root node in the document) to target a DOM element or collection of elements:

> $x('/html/body/div') // Returns an array of all divs in the top level of the body tag

This function probably isn’t super useful to most people, as they may be running jQuery (or using the $$() function) that allows them to more easily select the DOM nodes they’re interested in, but it is available for use, and allows you to be specific about the absolute path you’re interested in.

The last function I’ll cover here is the inspect() function, which is a nice quick way to jump from the console to the DOM inspector, and works in conjunction with the $ reference objects. This means we can now do something like this:

function foo (tagName) {
    return document.getElementsByTagName(tagName)[0];
}
> inspect(foo('body'));
> foo('div')
> inspect($_);

Voila! We now jump instantly to the DOM inspector when we run inspect() against the return of those function calls, whether we’re passing the function call directly as a parameter, or calling the foo() function on its own line and then calling the inspect() function with $_ as its parameter. It will inspect whatever DOM node you pass to it, jumping you over to the Elements tab to view it in your DOM inspector.

Hopefully you’ve learned some stuff you didn’t already know when it comes to working with the javascript console. I’ll be adding more posts to this series, covering each of the individual tabs available in most developer tool packages, and trying to highlight the most useful features of each of them. That way you can help improve your own debugging skills, and help save your QA team as much headache as possible.

Leave a Reply

Your email address will not be published. Required fields are marked *