Thursday, March 19, 2015

Viewing JavaScript console output on mobile devices

The Problem

Most mobile devices come with little if any debugging support. Debugging is usually done by connecting the device to a host computer (e.g., iPhone to a Mac) and using tools on the host computer. That's great if you have an iPhone and a Mac. But what if you Just Want to see what the JavaScript console is putting out, since you went to the trouble to put so much helpful debug information there?

The Solution

Give your console some soul

...Is barely even worth of a blog post, or 'blst' for short (as the kids here at SXSW are saying this week). Essentially, the secret is to hook the console's log function, then use it for our own purposes. At that point we can choose to forward the log statements on to the console, or 'swallow' them and only let our code have access.

The magic happens with just a few lines of code:


// console.log is just a function... store a reference
var oldf = console.log;

// redefine it and insert our own code
console.log = function () {

    // use the console output for our own purposes 
    someFunctionToShowConsole(arguments[0]);

    // also send it to the console, if we want
    oldf.apply(console, arguments);
}

Everything else is just window dressing. But, in order to give us a nice little debug window that doesn't take up too much of the page and lets us scroll etc., etc., we'll wrap it all up in a div docked to the bottom of the page, and show timestamps for log entries, and add a way to clear the console if the log gets too long.

When we're done, it should look something like this:


The 'real' (Chrome) console is on the right, our faux console is on the left.

The full code is below, and any updates will be posted to https://gist.github.com/jasonbrice/8131e38c14695afb4127


// set an identifier for the console div
var consoleId = 'debug_console';

// initialize the debug console, dock to bottom of page
var debugConsole = function () {

    var body = document.getElementsByTagName('body')[0];

    // since this function also gets called on clear(),
    // we'll remove the div if it exists so we can re-add it
    if (document.getElementById(consoleId))
        body.removeChild(document.getElementById(consoleId));

    // create new div and set whatever attributes we want
    var div = document.createElement('div');
    div.id = consoleId;

    // dock to bottom of page. styles are merely a matter of preference: move, color, change to suit.
    div.setAttribute('style',
        'position: fixed; bottom: 0px; left: 0px; width: 100%; height: 200px; color: #fff; background: #666; overflow: scroll;'
    );

    // make our new div part of the DOM
    body.appendChild(div);

    // add a link to call this function
    var href = document.createElement('a');
    href.innerHTML =
        "<a href='javascript:debugConsole();' style='text-decoration: none;'>" +
        "<span style='text-decoration: none; color: white;'>Clear</span></a><br/>";

    div.appendChild(href);

    // hook the console output function
    var oldf = console.log;
    console.log = function () {

        // use the console output for our own purposes
        div.innerHTML +=
            '<br/><span style="font-size: 12px; font-family: monospace;">' +
            timestamp() + '.$ ' + arguments[0] + '</span>';

        // also send it to the console
        oldf.apply(console, arguments);
    }
}

// timestamp adapted from https://gist.github.com/hurjas/2660489
var timestamp = function timeStamp() {

        // Create a date object with the current time
        var now = new Date();

        // Create an array with the current month, day and time
        var date = [now.getFullYear(), (now.getMonth() < 9 ? "0" : "") +
            (now.getMonth() + 1), now.getDate()
        ];

        // Create an array with the current hour, minute and second
        var time = [now.getHours(), now.getMinutes(), now.getSeconds()];

        // Determine AM or PM suffix based on the hour
        var suffix = (time[0] < 12) ? "AM" : "PM";

        // Convert hour from military time
        time[0] = (time[0] < 12) ? time[0] : time[0] - 12;

        // If hour is 0, set it to 12
        time[0] = time[0] || 12;

        // If seconds and minutes are less than 10, add a zero
        for (var i = 1; i < 3; i++) {
            if (time[i] < 10) {
                time[i] = "0" + time[i];
            }
        }

        // Return the formatted string
        return date.join(".") + " " + time.join(":") + " " + suffix;
    }
    // initialize the console once the rest of the

// document has finished loading
window.onload = debugConsole;

Conclusion

Even in 2015, there is no nice and convenient way to debug web pages on mobile devices. This solution would be even cooler if we could look at raw code and set breakpoints. But for now we can at least know what the console is spitting out, which, let's face it, is probably how the majority of NodeJS debugging is done.

Happy debugging, and if you like it, steal it!