How to create your own jQuery

Posted by

    While jQuery is a fantastic library, I also think that front-end developers rely on it a little too much. This article will show you how to get started on your own library. Not because you should replace jQuery, but rather to improve your knowledge about Javascript and the DOM.

    Why do we need jQuery?

    jQuery has made our lives as front-end developers a lot easier by covering up a lot of the differences between browsers. Developers no longer have to worry about older browsers, it just works. It also has a very easy to use fluent interface.

    Why might we not use jQuery?

    jQuery is almost 100kb minified. This can be very significant if you only use less than 10% of the features of jQuery. I very often find myself only using some querying, adding and removing classes and a few events. In these cases I think it might be worth considering rolling your own.

    Creating our own library

    Enough talk, lets create our own tiny version of jQuery. It will obviously not have all the features of jQuery, but it will be a good start and hopefully teach you a thing or two about jQuery and Javascript in general.

    Naming our library

    Since I am not very creative I will call my library dQuery both since my name is Dag and we are working with the DOM.

    Browser support

    We will aim for supporting IE8+, Chrome and Firefox.

    Collection class

    We need a collection class that will encapsulate our DOM elements and contain all our methods for manipulating the DOM.

        function DQueryCollection(elements) {
            this.elements = elements;
        } 
    

     

    We can now create objects representing a collection of DOM elements. Already we can use it like this:

        new DQueryCollection(document.querySelectorAll(".my-class"));
    

     

    Easier querying

    In order to make querying the DOM easier, we will adopt jQuery's $ symbol and wrap querySelectorAll inside it.

        var $ = function(input) {
            // Check if input is a DOM node
            if (input.nodeName) {
               return new DQueryCollection([input]);
            }
            // If not we assume it is a selector
            return new DQueryCollection(document.querySelectorAll(input));
        };
    

     

    Our function now supports two inputs, either a selector or a single DOM element. We assume that a DOM element was provided if we find the nodeName property. We need this to support $(this).

    Our first method

    Right now our code doesn't do much, so let's implement a method for looping all elements.

    It makes sense to implement this first, since we can use it for a lot of our other methods.

        function DQueryCollection(elements) {
            this.elements = elements;
            
            this.each = function(callback) {
                for (var i = 0; i < this.elements.length; i++) {
                    callback.call(this.elements[i], i);
                }
                return this;
            };
        }
    

     

    Finally we have something we can use:

        $(".my-class").each(function(i){
            console.log($(this));
        });

     

    Setting or reading text content of elements

    Let's create another method. This time we want one to set the text content of our elements. This code goes inside our DQueryCollection function.

        this.text = function(t) {
            if (t === undefined) {
                // No parameter, lets return the text of the first element
                if (this.elements.length < 1) return "";
                if (this.elements[0].textContent !== undefined) return this.elements[0].textContent;
                else return this.elements[0].innerText;
            }
            this.each(function(){
                if (this.textContent !== undefined) this.textContent = t;
                else this.innerText = t;
            });
            return this;
        };

     

    Don't be confused by the use of this. Outside of our each-callback it represents the DQueryCollection. Inside the callback it represent a single DOM element.

    Try it by running this snippet:

        $(".my-class").text("new text!!!");
    

     

    Note that the textContent property is not supported in IE8. For IE8 we use innerText instead.

    Adding a class to elements

    Our final method in this article will be addClass, another essential jQuery feature.

        this.addClass = function(c) {
            this.each(function(){
                // Split our className property into an array of classes
                var classes = this.className.split(/\s/);
                // No need to do anything if the class is already there
                if (classes.indexOf(c) > -1) return;
                if (classes.length === 0) this.className = c;
                this.className += " " + c;
            });
            return this;
        };
    

     

    Putting it all together

    Although we have only implemented a tiny fraction of the jQuery features, I hope you are starting to get the point.

    Right now the most advanced thing we can do is something like this:

        $(".my-class")
            .text("New text")
            .addClass("another-class");
    

     

    The final code looks like this:

        var $ = function(input) {
            if (input.nodeName) {
                return new DQueryCollection([input]);
            }
            return new DQueryCollection(document.querySelectorAll(input));
        };
    
        function DQueryCollection(elements) {
            this.elements = elements;
    
            this.each = function(callback) {
                for (var i = 0; i < this.elements.length; i++) {
                    callback.call(this.elements[i], i);
                }
                return this;
            };
    
            this.text = function(t) {
                if (t === undefined) {
                    // No parameter, lets return the text of the first element
                    if (this.elements.length < 1) return "";
                    if (this.elements[0].textContent !== undefined) return this.elements[0].textContent;
                    else return this.elements[0].innerText;
                }
                this.each(function(){
                    if (this.textContent !== undefined) this.textContent = t;
                    else this.innerText = t;
                });
                return this;
            };
    
            this.addClass = function(c) {
                this.each(function(){
                    // Split our className property into an array of classes
                    var classes = this.className.split(/\s/);
                    // No need to do anything if the class is already there
                    if (classes.indexOf(c) > -1) return;
                    if (classes.length === 0) this.className = c;
                    this.className += " " + c;
                });
                return this;
            };
        }

     

    Conclusion

    As you can see from this small example, it is not that hard to do DOM querying and manipulation without jQuery. Even though you will continue to use jQuery in the future, it can be very helpful to know when and how to roll your own.

    Warning

    I did skip a lot of things you should put into a real library, such as validating input. There may also be deviations from the jQuery API.

    The methods we implemented were also very easy. Implementing jQuery's event handling would be a lot more difficult.

    The code examples are only intended for learning purposes and not use in production.       

    Comments