Learning JavaScript By Building Your Own Version Of JQuery – Part 1

JQuery is one of the most popular libraries on the web. Although it seems to be in a decline amongst serious JavaScript engineers in favour of solutions which allow for better engineering, such as React, JQuery had a big impact on JavaScript for many years. Since it’s so well known, I thought getting you to write your own version would feel like a big achievement.

I’ll add a huge caveat here by saying you are only going to be implementing a very cut down version of the JQuery library. As well as only building a small subset of the functionality, you won’t be worrying about cross browser compatibility or performance, two things that I’m sure take up a huge chunk of the real JQuery’s source code.

First let’s look at a very simple operation in JQuery, and then you can replicate it. The simplest thing I can think of is adding a click handler based on a selector:

$('a').click(function () { console.log('clicked', this); });

This finds all elements based on the given selector, a, and adds a click handler to each of them. The selector will match all a tags (links) on the page, and add your click handler to each of them.

I setup a jsbin with the following HTML:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <script src="https://code.jquery.com/jquery-3.1.0.js">
  <title>JS Bin</title>
</head>
<body>
  <a href="#" id="link1">Link 1</a>
  <a href="#" id="link2">Link 2</a>
</body>
</html>

Note I load JQuery in the head section. I pasted the JQuery snippet to add the click handlers in the JS section of JSBin, and then clicking the links gave me the following console output:

As you can see, the output is ‘clicked’ followed by the DOM node for the link that was clicked on. So now I’m going to show you how to replicate this functionality in your own version of JQuery.

Let’s break the call to JQuery down. First you haveĀ $(‘a’). You can see from this that JQuery $ is a function, which takes as an argument a string (not 100% true, but you’ll handle different argument types in the future) which represents a DOM selector. Next comes .click(…). This is a function call to add the click handler to the selector. From this you can determine that the return type of the $ function must be some kind of object, and that object must have a .click() function.

I’m sure you’re keen to get to the code, so here is the code for your version of JQuery’s $ function. You can call your version _$ to distinguish it from the real JQuery.

function _$ (selector) {
  
}

This is just the function that takes a selector, it doesn’t really do much yet. It needs to return an object with a click function. Add that to it:

function _$ (selector) {
  return {
     click: (handler) => {
     }
  };
}

You will now be able to do:

_$('a').click(function () { console.log('clicked', this); });

At this point it won’t actually work in terms of adding a click handler to all links as the click function is empty, but it won’t give an error either. Now add the body of the click method. It will need to use the selector to find all elements that match, and then add the click handler to each of them in turn. With JQuery we can add click handlers to all elements which match the selector at once. With vanilla JavaScript DOM functions we have to do it one DOM node at a time. This will allow us to replicate this JQuery functionality, simply by looping over all matched elements and using .addEventListener() on each element. Your code should look something like this:

function _$ (selector) {
  return {
     click: (handler) => {
       // Find all DOM nodes matching the selector
       // using document.querySelectorAll
       const nodes = document.querySelectorAll(selector);
       // Transform the NodeList object returned (nodes) into an array
       // Iterate the array, adding the click handler to each node in it
       [...nodes].forEach((node) => node.addEventListener('click', handler));
     }
}

Now doing:

_$('a').click(function () { console.log('clicked', this); });

Will add all of the click handlers. Don’t take my word for it… Try it out on JSBin. Please note that due to the use of ES6 features, you’ll need to set the JavaScript pane in JSBin to ‘ES6 / Babel’ for this code to work:

If you aren’t yet familiar with the ES6 features in the above code, I have videos on them (arrow functions & destructuring) on youtube.

The click function still doesn’t work like JQuery in one regard. JQuery allows you to chain function calls, so you could, for example add two click handlers by doing:

$('a')
  .click(function () { console.log(this) })
  .click(() => alert('clicked'));

Since your click function doesn’t return anything, this would error if you tried to use your $_ function to do this. The simple solution to his is we need to return the same object from the click function as we do from the $_ function itself. You can do this by simply assigning the object to a const, and returning it both from $_ and from click. Do it like this:

function _$ (selector) {
  const actions = {
     click: (handler) => {
       const nodes = document.querySelectorAll(selector);
       [...nodes].forEach((node) => node.addEventListener('click', handler));
       return actions;
     }
  };
  return actions;
}

Even though click is within the actions object itself, it is a closure over the scope in which actions is declared, therefore we can access the actions object from within the click function.

Next time I’ll show you a trick that will make adding many other types of event handler easy.

3 comments

Leave a Reply

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