Well that surprised me

I recently started work on a web-based app for smart phones. Since it was explicitly for mobiles and explicitly not for desktop devices (and I was building it in my own time rather than for a client) I decided not to use jQuery. I’d read a study that showed how large amounts of JavaScript on a page can reduce a smartphone’s battery charge and I knew I wouldn’t be using most of the jQuery library, so it had to go.

I knew that some basic level of DOM interaction would be needed and performance testing shows that document.getElementById is more efficient than document.querySelectorAll – seriously, it’s not even close. In fact, document.getElementById, document.getElementsByTagName and document.getElementsByClassName are all faster than document.querySelector and document.querySelectorAll. So, I came up with a little theory: what if I checked a string and passed it to the correct getElement* function?

The code itself would be pretty straight forward. A few basic methods to handle the interactions, convert the results into an array for consistency and a generic helper to decide which method should be passed the request.

var dom = {

    toArray: function (nodes) {

        var array = [];

        if (nodes) {

            array = typeof nodes.length === 'number' ?
                    Array.prototype.slice.call(nodes) :
                    [nodes];

        }

        return array;

    },

    byId: function (id, context) {

        var start = context || document;

        return this.toArray(start.getElementById(id));

    },

    byClass: function (className, context) {

        var start = context || document;

        return this.toArray(start.getElementsByClassName(className));

    },

    byTag: function (tag, context) {

        var start = context || document;

        return this.toArray(start.getElementsByTagName(tag));

    },

    byQuery: function (query, context) {

        var start = context || document;

        return this.toArray(start.querySelectorAll(query));

    },

    get: function (query, context) {

        var found = null,
            name  = query.slice(1);

        if (/^#\w+$/.test(query)) {
            found = this.byId(name, context);
        } else if (/^\.\w+$/.test(query)) {
            found = this.byClass(name, context);
        } else if (/^\w+$/.test(query)) {
            found = this.byTag(query, context);
        } else {
            found = this.byQuery(query, context);
        }

        return found;

    }

};

Since efficiency was the whole point of the exercise, I performance tested this object. The results surprised me. My dom.get() method was slower than dom.toArray(document.querySelectorAll()). It seems that the additional processing required to check a string and pass it to the correct method outweighs any benefit gained from not simply passing a string to document.querySelectorAll in the first place.

And it seemed like such a good theory, too …

Leave a Reply

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