Javascript

How to Stop Using jQuery

Sponsor

jQuery is a widely used tool across the web, and great for accomplishing things quickly in frontend Javascript development. When I started coding, jQuery was an amazing tool for helping me accomplish my goals fast. It also helped that Javascript was still a developing language - I remember having to support IE7, which didn't have any support for querySelectorAll for example.

Up until relatively recently, jQuery's functionality was still really useful. Over the years, however, vanilla JS has slowly caught up, old browsers have died out, and chances are, jQuery is now working against some of the objectives of your website or app, speed being the most obvious one.

Let's take a look at the major things jQuery has been good for in the past, and how you can slowly wean yourself off them (along with a speed comparison).

Selectors

jQuery's major strength was always DOM selection and traversal. There is a slight problem with jQuery selection, however, and that is it hides the true amount of operations you are doing in your code. This can lead to bloated and slow websites, as you utilise what seems like a simple syntax, but underneath, is running many loops.

javascript Copy
$('.button').addClass('start');

From the above code, it seems like not much is happening, but jQuery is going through every .button DOM element individually and adding a class. If you suddenly have hundreds of .button elements, then under the covers jQuery is doing a lot of processing. This inevitably slows down your application or website.

It gets even worse if you decide to chain selectors, which can result in many hundreds of operations for what seems like a straight forward DOM traversal. Let's take a look at the vanilla JS alternative:

javascript Copy
let selector = document.querySelectorAll('.button'); // Modern browsers selector.forEach(function(item) { item.classList.add('start'); }); // If you need older versions of IE Array.prototype.slice.call(selector).forEach(function(item) { item.classList.add('start'); });

querySelector gives you all the flexibility of CSS selectors, and the forEach statement makes it clear how many operations you are doing. Only want to affect the first element? Use querySelector. Want to affect an ID? Use getElementById. How much faster is vanilla JS? Take a look:

jQuery Selector Speed Comparison with Vanilla JS

Attribute Selection

Perhaps the most similar of them all, attribute selection looks basically the same for jQuery and vanilla JS:

javascript Copy
$('.button').attr('data-width'); document.querySelector('.button').getAttribute('data-width');

But the speed difference tells a different story:

jQuery Attribute Selection Speed Comparison with Vanilla JS

🥈 Fade In/out

Fade in and out was, at one time, a major thing tying me to jQuery, but it's so simple to replace - just add a class where the class transitions to an opacity of 0, or 1.

javascript Copy
document.querySelector('.button').classList.add('fade-out');
css Copy
.button { transition: opacity 0.2s ease-out; opacity: 1; } .fade-out { opacity: 0; }

And how much faster is that than fadeOut()? Although vanilla JS is more than 300 times faster, fadeOut() still only clinches silver in our jQuery speed test.

jQuery Fade Out Speed Comparison with Vanilla JS

Parent and Child Matching

Another crutch, and another reason to stick with jQuery. Matching up and down the DOM tree is so much easier with a simple piece of jQuery like this, which matches any parent element of .button:

javascript Copy
$('.button').parents('#container');

But does it have to be this way? For two simple functions like parents() and children(), we can surely write our own. Here are some functions I put together that do the same thing as this function, in vanilla JS. Here they are for children and parents:

javascript Copy
// parent: matches the first matching parent in the DOM Tree // @el[string]: el you want to find a parent of // @match[string]: css selector for the matching element // @last[boolean]: if true, only the last element will be returned. If false, all parents to that element are returned const parent = function(el, match, last) { var result = []; for (var p = el && el.parentElement; p; p = p.parentElement) { result.push(p); if(p.matches(match)) { break; } } if(last == 1) { return result[result.length - 1]; } else { return result; } }; // child: matches the first matching child in the DOM Tree // @el[string]: el you want to find a children of // @match[string]: css selector for the matching element // @last[<]boolean]: if true, only the last element will be returned. If false, all children to that element are returned const child = function(el, match, last) { var result = []; for (var p = 0; p < [...el.children].length; ++p) { result.push([...el.children][p]); if([...el.children][p].matches(match)) { break; } } if(last == 1) { return result[result.length - 1]; } else { return result; } };

And how much faster is that? It depends on your DOM size, but on a DOM tree with five levels, here are the results. Again, vanilla JS clinches the speed victory:

jQuery DOM Tree Parent Selection Speed Comparison with Vanilla JS

For Each

jQuery comes with its own forEach, which can be used in tandem with a jQuery selector, or an object or array. Here's what both of them look like:

javascript Copy
// Vanilla JS ForEach document.querySelectorAll('.button').forEach(function(item) { item.classList.add('start'); }); // jQuery $.each($('.button'), function(k, v) { $(v).addClass('start'); });

Obviously, the jQuery forEach is a little more intuitive. It will automatically parse objects, for example, whereas in vanilla JS you have to use Object.keys.

However, as you may have guessed, the jQuery version is more inefficient than vanilla JS. In fact, jQuery runs about twice as slow:

jQuery For Each Comparison with Vanilla JS

Ajax/POST/GET

Ajax requests kept me coming back to jQuery for much longer than I really needed to. However, it's not too hard to construct your own Ajax requests with vanilla JS. For example, imagine you want to POST something. In jQuery you write:

javascript Copy
$.ajax({ type: 'POST', data: data, url: '/some/url/to/data', success: function(e) {} });

In vanilla JS, you have to write this:

javascript Copy
fetch(url, { type: 'POST', headers: {}, // If you want headers body: data }).then((data) => { // do something with the data // Same as success: function() in jQuery });

👑 Creating New Elements

Creating new elements in vanilla JS can seem a little bit harder, but the benefits can be huge in terms of processing speed. I would also argue creating with vanilla JS makes your code a lot clearer. Here is how creation of a new element works in vanilla JS and jQuery:

javascript Copy
// Vanilla JS let el = document.createElement('div'); el.classList.add('button'); el.innerHTML = '<div><div><div></div></div></div>'; document.body.appendChild(el); // jQuery $('body').append('<div class="button"><div><div><div></div></div></div></div>');

As you might have guessed, this takes the crown for our slowest jQuery process, with vanilla JS coming in at 800x faster.

jQuery Create Element Speed Comparison with Vanilla JS

Conclusion

When I started this, I was expecting minor speed improvements with vanilla JS - but the differences here can have a big effect on the overall speed of your website or app. Multiply these small changes by 100s of jQuery calls, and suddenly your website becomes slow and more unresponsive. Not only that, but jQuery isn't exactly tiny either, coming in at roughly ~90kb.

Changing an ongoing project from jQuery to vanilla JS can be quite a demanding undertaking, but next time you start something new, consider doing it without jQuery. It might make all the difference.

Last Updated Saturday, 26 December 2020

Subscribe to Newsletter

Subscribe to stay up to date with our latest web development and software engineering posts via email. You can opt out at any time.

Not a valid email