Replacing jQuery with Vanilla JavaScript

vanilla_js_jquery_header

By Zachary Brady

It seems as if jQuery has become synonymous with JavaScript. Since its introduction in 2006, jQuery has caused a revolution in front-end scripting. It has made it easier for newcomers to get up and running, decreased prototyping and development time, and has opened the door into an endless supply of new UX and UI elements. It’s no wonder that Stackoverflow’s JavaScript related questions are primarily answered with jQuery solutions. jQuery has embed itself in our scripting grammar. However, it may be time to go back to the basics.

For some sites, you may be using jQuery for some basic functionality. In this article, we’ll explore the opportunity to replace jQuery in those situations with plain vanilla JavaScript. As we’ll see, it can offer some potential performance benefits and, perhaps more importantly, an opportunity to learn.

Benefits of Vanilla JavaScript

Remaking your jQuery repertoire with pure vanilla JavaScript may feel like reinventing the wheel, and in some ways it will be, but the benefits of doing so are numerous. The most obvious benefit will be in reducing the strain on your pages’ load times. Compressed jQuery stands at only 38.5KB. Alone this file size is far from being a deal breaker but coupled with often-bloated jQuery plugins your page may start to feel sluggish. If you plan and using just a few lines of jQuery, jQuery’s extra weight can start to seem unnecessary. Having a roadmap of how to replace jQuery in your production code is important if performance optimization is important to you and your team.

The reverse engineering needed to switch back to vanilla JS has its own benefits as well. Many of jQuery’s most useful capabilities are gradually being incorporated into vanilla JS through the steady influx of new HTML5 APIs, offering an opportunity to learn new features. When you can’t find the functionality you need in an HTML5 API, building a function from scratch is also a great way to dive into unexplored territory. For all the greatness that jQuery is, relying on it as heavily as we do may be blinding us from seeing both what’s new and what has been there all along in standard JavaScript.

Breaking away from jQuery is also a great way to take control of how you code. If a jQuery function doesn’t do exactly what you want it to do, a good understanding of JavaScript can help you remake the function in your own image.

Moving away from jQuery will require some elbow grease but is much easier than it seems. Below we’ll see just how powerful vanilla JavaScript coupled with new HTML5 APIs are as we backwards engineer a technique I use in most of my projects.

The Simple Button

One of the most basic elements of UI design is the button. Buttons create a reaction to an action. Websites are full of buttons in the form of links. The act of clicking on a link has the reaction of bringing the user to someplace new whether that is located on the same page, another page on the site, or an external webpage. Buttons are one of the most intuitive interfaces and are therefore the best place to start when expanding a website’s experience.

Sometimes we want a button to produce a stylistic change on the current page whether that be hiding/revealing content, changing the quality of a designed element, or something else. I like to view this type of interaction in terms of a “resting state,” the initial view of whatever aspect of the page that is being affect, and an “active state,” the view of the aspect after the button has been clicked. The button, in this case, acts as a trigger to switch between the two states.

replacingjquery1 replacingjquery2

To accomplish this I use a blend of JavaScript and CSS. JavaScript is used to add and remove a CSS class named “active” from both the trigger and the target, while my CSS contains styles related to the two states. While I could use JavaScript to directly affect the style of the target element instead of adding a class, I find that using CSS (almost) exclusively to handle my style related needs to be a much cleaner approach and results in code that is far easier to reuse throughout and across projects.

We’ll be using the following simplified HTML throughout this tutorial. You can imagine any styles you want.

<a href="#target" id="trigger">The Trigger</a>
<div id="target">The Target</div>

Now that we’re all on the same page, lets first see how we’d set this up using jQuery.

Setting Up a jQuery Toggle

On its surface the jQuery implementation is very simple and straightforward. One of jQuery’s key strengths is how easy it is to understand making it a great tool for team development. However, as we’ll see later on, that doesn’t mean that vanilla JavaScript can’t be straightforward as well.

$('#trigger').on('click', function(event) {
  // cache your selectors
  var $trigger = $(this);
  var targetName = $trigger.attr('href');
  var $target = $(targetName);
  
  // don't mess with things if there is no target
  if (!$target.length) {
    return;
  }
 
  // abort events properly, returning false does multiple things
  event.preventDefault();
  event.stopImmediatePropagation();
  
  var active = $trigger.hasClass('active');
  $trigger.toggleClass('active', !active);
  $target.toggleClass('active', !active);
});

The above code adds a click event listener to the “trigger” element. When the trigger is clicked, the code first retrieves the reference to the target element from the trigger’s href attribute with the attr() function and saves it to a variable. It then asks the question: “does the trigger currently have the class ‘active’”, which we discover with the hasClass() function in an if/else statement. The class “active” is added or removed, as necessary, from both the trigger and the target using toggleClass(). The function also calls event.preventDefault() and event.stopImmediatePropagation() to prevent standard link functionality.

While the amount of code you need to write is quite small with this jQuery based approach, you must include the entire jQuery library in your project. If this is the only place in your project where you are using jQuery (and in some of my old projects it was), it’s time to backwards engineer a vanilla JavaScript approach to decrease bloat.

Backwards Engineering jQuery Functions

Let’s take a look at the jQuery functionality from before, dissect it, and recreate it without jQuery. We’ll start with the code within the click event first, which we’ll place in a function, and then apply that function to a click event using vanilla JavaScript.

In fact it is fairly simple to recreate the above jQuery thanks to the classlist HTML5 API (https://developer.mozilla.org/en-US/docs/Web/API/Element.classList) and other APIs. Please note that for support for Internet Explorer versions less than 10 you must use a shim, which is thankfully listed on the Mozilla developer page, for the classlist API. Even with the shim, support only goes back to Internet Explorer 8. I have found that this is not a concern for my projects as long as older browsers can still get all the HTML content. If this is an issue, it is possible to replicate this functionality without the classlist that has support for older IE versions.

There are two main ways to go about replicating the functionality. The first that we’ll go over looks very similar to the jQuery approach but, of course, sans-jQuery. Below is the code as a function that we’ll use in an event listener.

function activeState(trigger) {
  var targetName = trigger.getAttribute('href').replace('#','');
  var target = document.getElementById(targetName);
  
  // don't mess with things if there is no target
  if (!target) {
    return;
  }
  
  if (trigger.classList.contains('active')) {
    trigger.classList.remove('active');
    target.classList.remove('active');
  } else {
    trigger.classList.add('active');
    target.classList.add('active');
  }
};

We begin by creating a function object named activeState, which receives a DOM reference to the trigger element.

Inside our function, we first replace the jQuery attr() function with the getAttribute() function with the value “href”. getAttribute() is part of the attr API (https://developer.mozilla.org/en-US/docs/Web/API/Attr). We need the element ID referenced in the trigger’s href without the leading # and so we also use the replace() function (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) to accomplish this. Using the name of the target element’s ID we can retrieve a reference to that element using document.getElementById().

We then directly replace the hasClass(), removeClass(), and addClass() functions respectively with classList.contains(), classList.remove(), and classList.add(). We can now apply this function to an event listener attached to the trigger element.

var trigger = document.getElementById('trigger');
trigger.addEventListener('click', function(event) {
  // abort the link's default action.
  event.preventDefault();
  event.stopImmediatePropagation();
  
  activeState(this);
}, true);

As you can see, we have now replicated the functionality and almost exactly replicated the appearance of our jQuery code without the use of the jQuery library.

Going Above and Beyond

We don’t have to stop there either. The classList API also includes a function classList.toggle(), which will take care of the logic for us so that we don’t have to use an if/else statement.

function activeState(trigger) {
  var targetName = trigger.getAttribute('href').replace('#', '');
  var target = document.getElementById(targetName);
  
  if (!target) {
    return;
  }
  
  trigger.classList.toggle('active');
  target.classList.toggle('active');
}

The toggle function asks the applied element if it has a specific class. If it contains that class then the class is removed but if it doesn’t the class is added. We accomplished this task before with a combination of and if/else statement and the functions classList.remove() and classList.add(). With classList.toggle() we’re able to consolidate this into two lines. Of course, this code assumes that the active classes of both the trigger and the target are in sync, so we might improve it by considering and handling a situation where they may not be.

Finding Your Own Path

Its unlikely that jQuery will disappear anytime soon, and it shouldn’t, but its important that we don’t use jQuery as a crutch. Like any code library it has its invaluable strengths. One of jQuery’s greatest strengths is in how it simplifies cross browser support, especially in places where achieving support and consistency is difficult, such as with the various forms of AJAX. However, as older browsers are gradually abandoned and new HTML5 APIs gain wider support, we have to make sure we’re not using the jQuery library unnecessarily.

There also new Javascript libraries, with more specific purposes, popping up every day. The Greensock Library, for instance, is a lightweight library specially crafted for handling complex animations such as tweening. Staying up to date on similar libraries that replace common jQuery use cases is important in ensuring that you’re using the right tools for the job.

And, also, who doesn’t like a bit of a challenge. Just because the brilliant contributors to jQuery have already done something doesn’t mean you can’t try to do it as well. Trying to backward engineer your favorite jQuery functions is a great way to push yourself as a developer. Just remember that jQuery is just JavaScript in a very nice package. Anything that jQuery can do vanilla JavaScript can do as well. Keep yourself open to experimentation as you do this and you may make some amazing insights.

As you create your own functions, or repurpose the work of others, it’s wise to store your code and keep documentation for it. Start to create your own JavaScript libraries that are made up of functions and tricks common to your toolbox. Keeping your library small and well documented will help you customize it for different projects and will make it easy to remove the parts you don’t need.

There are some amazing resources out there for figuring out vanilla JavaScript best practices. I am far from alone in highly suggest that you up Javascript: The Good Parts by Douglas Crockford as soon as possible, if you haven’t already. If you’re crunched for time but want to remove jQuery from a project I suggest checking out You Might Not Need jQuery to see if they have any solutions. Finally, there are so many articles already written on this topic that look at different use cases including Anthony Colangelo’s article Choosing Vanilla Javascript and Louis Lazaris’s Thinking Inside the Box with Vanilla Javascript.

The idea is not to throw jQuery away but rather to increase are own abilities and codebases so that we don’t have to rely so heavily on jQuery. Taking the leap into the world of vanilla jQuery may seem intimidating but is beyond worth the minor stress. As front-end developers we shouldn’t be satisfied with solutions given to us out of the box. We need to challenge ourselves to expand our own abilities, improve the code and strategies used in our projects, and, possibly, even improve front-end development as a whole.

Editor’s note: special thanks to Rodney Rehm for is help in providing suggestions to fix and improve the included code examples subsequent to its original publication.

Modern Web Newsletter

Subscribe to receive the Modern Web tutorials, sent out every second Wednesday.

  • Luis Martins

    You do realize Vanilla.js is a joke right?
    Good post, being a designer trying to properly learn how to code JS, this pretty much reflects my experience. Blindly relying on jQuery limits your knowledge and learning opportunities. My approach at the moment is to have jQuery available in the project where makes sense, but still trying to avoid jQuery functionality in those same projects as much as possible. This allows for a smoother transition, while forcing myself to keep learning vanilla javascript.

  • Sylvain Pollet-Villard

    No mention to query, queryAll, querySelector, querySelectorAll ? Yet selector function is jQuery killer feature. Also, note that jQuery uses addEventListener and not on[event] attributes. The jQuery code examples in this article can be widely shortened, I suspect you have deliberately hidden some of the best aspects of jQuery like chaining and permissiveness of the API. For example, the first example can be reduced to :
    $(‘#trigger’).on(‘click’, function(){
    $(this).add(this.href).toggleClass(‘active’);
    return false;
    });

    • http://Hayesmaker.com Hayesmaker

      Grats on missing the point

  • remotesynth

    Just an FYI, I’ve made updates the the example code that include some improvements and some corrections.

  • Desmond Wolf

    Just out of interest, in your original jQuery $(‘#trigger’).on() method, why do you pass the value of this into the jQuery function? Seems redundant – wouldn’t just using this directly do the same thing – if you pass a jQuery object to $() rather than a selector string it just clones it, right?

  • http://naofumi.castle104.com Naofumi

    This is very similar to the approach that I’ve been using for mobile development where IE is not an issue, with the exception that I never really felt comfortable with jQuery to begin with.

    I use jQuery to write some shims for IE9 and below though. Especially for custom events.

  • http://twitter.com/ossamaz ossama

    Nice post.
    jQuery is one of the key factors that helped the web move foreward over the last few years. That’s for sure :)
    but the thing is.. everyone knows how to write jQuery but not javascript and that’s the problem.

  • http://teknosains.com budyk

    nice points….

    guess what, in my country most of jobs offered in Web development required JQuery..it seems all website running in my region are using jQuery :| . for front-end developers learning native javascript could be a real pain

Top