Implement jQuery APIs Using Vanilla Javascript

July 27, 2018
js jQuery

In this article, we’re going to learn jQuery by implementing some simple APIs for our own jQuery version. It’s worth to understand the core and thus use the tool faster. Now let’s start by creating some basic functionalities.

Create a function

So firstly create a function that adds the specified class(es) to an element. It’s basically exactly the same thing we do with globals: We all know that adding tons of global variables is bad practice, so every library adds one global and hangs everything off of that.

function addClass(node, classes){
  classes.forEach(className => node.classList.add(className));
}

window.myJquery = {};
myJquery.addClass = addClass;

// "item2" is an id of a list item in html
addClass(item2,["green", "big"]);

Namespacing (design pattern)

It’s important to safeguard your code from breaking in the event of another script on the page using the same variable or method names as you are. In many programming languages, namespacing is a technique employed to avoid collisions with other objects or variables in the global namespace.

We can translate our codes using namespacing pattern by creating a global variable myJquery like this:

let myJquery = {}
myJquery.addClass = function(node, classes) {
  classes.forEach(className => node.classList.add(className));
}
// "item2" is an id of a list item in html
myJquery.addClass(item2,["green", "big"])

Use an element to call a function

We can still notice that the namespacing pattern seems not intuitive when calling a function, can we put the element before the function name instead of leaving it as an argument?

Yes there are two ways to do so.

  1. Extend the native DOM Node.prototype APIs:

    Node.prototype.addClass = function(classes) {
      classes.forEach(className=>this.classList.add(className));
    }
    
    item1.addClass.call(item1,["green", "big"])
    // item1.addClass(["green", "big"])
    

    Well, this is also a naive way that might cause naming collision problems.

  2. Create a new constructor function (no collision):

    function MyJquery(node){
      return{
        element: node,
        addClass: function (classes){
          classes.forEach(className => node.classList.add(className));
        }
      }
    
    
    let listItem3 = MyJquery(item3);
    listItem3.addClass(["red", "big"]);
    

    Now we can create an object with the MyJquery constructor function, which has access to all of the properties that the constructor returns.

Improvement: allow CSS selectors

jQuery offers a powerful set of tools for matching a set of elements in a document. We can improve our MyJquery by allowing parameters with CSS selectors. And let’s also add a setText method:

function MyJquery(nodeOrSelector){
  let nodes = {};
  if (typeof nodeOrSelector === 'string'){
    let elements = document.querySelectorAll(nodeOrSelector);
    for (let i = 0; i < elements.length; i++){
      nodes[i] = elements[i];
    }
    nodes.length = elements.length;
  }
  else if (nodeOrSelector instanceof Node){
    nodes={
      0: nodeOrSelector,
      length: 1
    }
  }

  nodes.addClass = function (classes){
    classes.forEach(className => {
      for (let i = 0; i < nodes.length; i++){
        nodes[i].classList.add(className);
      }
    });
  };

  nodes.setText = function (text){
    for (let i = 0; i < nodes.length; i++){
        nodes[i].textContent = text;
      }
  }

  return nodes;
}

let items = MyJquery("ul > li");
items.addClass(["red", "big"]);
items.setText("hi");

Add alias to our constructor

Simply adding a global variable $ and set it to MyJquery, we can create an alias to our version of jQuery.

window.$ = MyJquery;

$("ul > li").setText("hi"); 

Okay, now we have a basic understanding of how to create our own jQuery library by adding methods to the constructor. Actually jQuery works in the similar way:

typeof jQuery === "function"  // true
$('li').__proto__ ===  jQuery.prototype  // true.

So go ahead and read more on jQuery API docs, it’ll be a lot easier to understand now.

comments powered by Disqus