i am dustin diaz

a JavaScriptr...

boosh.

don't worry about it.

Roll out your own JavaScript Interfaces

There are times when using a JavaScript library is called for. Building large web applications that use a wide array of utility functions that help aid in developing multi-tiered class systems, advanced UI components, complex event models, and heavy use of DOM scripting helpers. Yep. Those are all great. However, there are other times when you don't need all that. And often what we end up doing is just importing a few of our favorite functions as globals, and work off those. But what ends up happening in this case is that we lose the particular style that these libraries offer. For instance, I'd still like to be able to do something like this without a library.

Sample application code

$('foo', 'bar').on('click', function(e) {

    $(this).css({

        color: 'green',

        fontSize: '2em'

    }).addClass('active');

});

Starting from scratch

Since we all have a love for the old dollar function introduced by Prototype, we can use that as a base.

Original Prototype $ function

function $() {

	var elements = [];

	for (var i = 0; i < arguments.length; i++) {

		var element = arguments[i];

		if (typeof element == 'string')

			element = document.getElementById(element);

		if (arguments.length == 1)

			return element;

		elements.push(element);

	}

	return elements;

}
However, instead of treating this as a normal function, we'll transform it into a constructor, then assign instance methods. Then of course to achieve chainability, all we have to do is simply return the instance reference. We'll start off by creating a private interface, then adapt the dollar function to return this private constructor.

Creating the interface

(function() {

  // private constructor

  function _$(els) {

    this.elements = [];

    for (var i=0; i<els.length; i++) {

      var element = els[i];

      if (typeof element == 'string') {

        element = document.getElementById(element);

      }

      this.elements.push(element);

    }

    return this;

  }



  _$.prototype = {

    each: function(fn) {

      for ( var i = 0, len = this.elements.length; i<len; ++i ) {

        fn.call(this, this.elements[i]);

      }

      return this;

    },

    setStyle: function(prop, val) {

      this.each(function(el) {

        el.style[prop] = val;

      });

      return this;

    },

    addClass: function(className) {

      this.each(function(el) {

        el.className += ' '+className;

      });

      return this;

    },

    on: function(type, fn) {

      var listen = function(el) {

        if (window.addEventListener) {

          el.addEventListener(type, fn, false);

        } else if (window.attachEvent) {

          el.attachEvent('on'+type, function() {

            fn.call(el, window.event);

          });

        }

      };

      this.each(function(el) {

        listen(el);

      });

      return this;

    },

    css: function(o) {

      var that = this;

      this.each(function(el) {

        for (var prop in o) {

          console.log(prop);

          that.setStyle(prop, o[prop]);

        }

      });

      return this;

    }

  };

  window.$ = function() {

    return new _$(arguments);

  }

})();

Short and concise! Check out an implementation of this code in action to see how easy it is to start rolling out your own personal interfaces. Cheers.

this is who i am

Hi, my name is Dustin Diaz and I'm an Engineer @ObviousCorp. Previously @Twitter, @Google, and @Yahoo, author of Strobist® Info co-author of JavaScript Design Patterns, co-creator of the Ender JavaScript Framework, a Photographer, and an amateur Mixologist. This is my website. Welcome!

On this site I write about JavaScript. You can also follow along with my open-source work on Github.

This site is optimized and works best in Microsoft Internet Explorer 6.