How I’m (Not) Using jQuery

Let me begin by saying that this is not yet another post about how you don’t need jQuery. In case you haven’t noticed, that’s already been covered. Roughly 8 million times:

Yep, this has been covered

I don’t want anyone to stop using jQuery. Use it. Love it. Be happy to keep on coding with it! However, as I’ve moved away from using it in my own code, I thought I’d share some details on how I’ve made that transition, specifically the “phases” of my move away and how I’m doing things now. I’ll also talk a bit about what I still struggle with.

I will remind people that I don’t do a lot of production work. Therefore, I’m not worried about older browsers typically. That makes my needs (probably) different from yours.

How I use jQuery

For me, a vast majority of my jQuery usage was:

  • Bootstrap code on page load
  • Listen for click or change events to things
  • Read values from form fields
  • Ajax stuff
  • Write HTML to the DOM

Certainly I did more than that, but in terms of what I was specifically using jQuery for, that probably covers 99% of it.

// “How I’m (Not) Using jQuery” is one of our top 5 JavaScript articles of 2017. See the full list here.

Page Load

Nearly every application I worked on started with this beauty:

$(document).ready(function() {
    // STUFF HERE
});

This was to the point where I had this as a snippet so I could drop it in files quickly. Heck, I even caught myself with this code in pages where I wasn’t even doing any JavaScript.

I replaced this with an event listener on DOMContentLoaded:

document.addEventListener('DOMContentLoaded', function() {
    // STUFF HERE

}, false);

The MDN docs for this event says:

The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.

This is an easy change. It is a bit more typing, but snippets in your editor can make that a non-issue. However, note that this is not a perfect replacement for jQuery’s document.ready functionality. Their docs mention this issue:

Most browsers provide similar functionality in the form of a DOMContentLoaded event. However, jQuery’s .ready() method differs in an important and useful way: If the DOM becomes ready and the browser fires DOMContentLoaded before the code calls .ready( handler ), the function handler will still be executed. In contrast, a DOMContentLoaded event listener added after the event fires is never executed.

The last sentence is the crucial one. I’ve never run into this issue myself, but it is something you will want to keep in mind.

Event Listeners

I already demonstrated it, but you simply use addEventListener and you’re good to go. The general format is (the thing).addEventListener('name of event', function) and, as I said above, while it is a bit more typing then jQuery’s version, it typically isn’t a big deal.

The only real problem I’ve had with this was something that came up recently. Imagine you want to listen for click events on products. Now imagine the products are loaded after the initial page load. jQuery provides support for this by letting you listen for an event on the document object, but then specify that you only care if the target was some specific item inside it. That basic form is:

$("#listOfProducts").on("click", ".productThumb", function() { 
    // STUFF
});

I recently had to do this in another app and realized I wasn’t sure how to get that done. This is what I ended up with:

document.addEventListener('click', function(e) {
    if(e.target.className !== 'editLink') return;
    e.preventDefault();

    // STUFF    
}, false);

Basically I just look at the event’s target data and see if it makes sense for my logic. In the case above, I’m looking if a CSS class “editLink” was applied, and if so, I continue on.

Getting DOM Items

This one’s the easiest. jQuery made it easy to get access to a DOM item with selectors: $(selector). Modern browsers provide similar support with querySelector and querySelectorAll. querySelector would be used when you know you are matching one item, like a form field or a div, and querySelectorAll would be used to match multiple items. I can’t honestly remember every using querySelectorAll although I’m sure I will in the future.

I prefer to prefix variables created with this API with $. So for example:

let $field1 = document.querySelector('#field1');
let $field2 = document.querySelector('#field2');

This just reminds me that the variables are pointers to DOM items.

Reading and Writing DOM Items

So after I’ve got access to something in the DOM, I typically have to read from them and set them. For a form field this is fairly easy – you just use .value. The only issue I think you will run into would be working with checkboxes and radio fields. I haven’t had to worry about that yet actually so therefore I’ve not had to deal with it.

Writing HTML inside a DOM element is also simple – instead of $something.html(string), you would use $something.innerHTML=string.

Again – jQuery definitely helps here with a simpler API and by covering edge cases, but I’ve been able to get by with the samples above.

Enter the Ajax

Ok, so you’ll notice everything so far has involved basic DOM manipulation. I found that easy enough and when I didn’t, I’d just hit up MDN and spend a few minutes reading. The main thing that held me from completely leaving jQuery was working with Ajax.

Doing Ajax “by hand” is not terribly difficult. You can read about it on (of course) MDN – AJAX – Getting Started. But I could never quite memorize the flow of working with the XMLHttpRequest object.

Luckily, the Fetch API came along, and while I’m still a bit shaky with it, I love the simplicity of the API. I’ve done a few blog posts already on it, but here is a quick example of how it looks:


fetch('/data.json')
.then(res => res.json())
.then(res => {
    // res is my data
});

You can do regular GET requests as well as POSTs with form data. Basically anything you could do with jQuery or vanilla XHR you can do with Fetch.

What’s Left?

Now that I’m comfortable getting, reading from, and manipulating DOM items and doing Ajax, the one area I’ve run into trouble with is DOM traversal. So for example, finding something in the DOM but actually wanting the previous or next item. jQuery makes this trivial with things like .prev() or .next(). There is a good resource for this and it’s one of those “you don’t need jQuery” sites. The site? youmightnotneedjquery.com. Nicely named, right? It has specific examples of .prev() and .next() and other DOM traversal items I rarely need, but I’m glad to know I can lookup when I need it.

Another useful resource, and one with an easy to remember name, is plainjs.com. It too contains a “jQuery to not-jQuery” index of resources for translating your code.

I’d be curious to hear how others are doing on their move from jQuery, if, of course, that’s what you’re actually doing. If you have specifically decided to not move from jQuery then I’d like to hear about that as well. Just drop me a comment below!

Comments

  • Pingback: How I’m (Not) Using jQuery – PipisCrew Official Homepage()

  • Pingback: Dew Drop - October 13, 2017 (#2581) - Morning Dew()

  • Pingback: FRONT-END DIGEST ( Oct 9 — Oct 15, 2017) | Zfort Group Blog()

  • kirilloid

    The second point about delegation events is a little more complicated.
    jQuery checks arbitrary selector (that can be written with .matches DOM API call) and it checks the selector not only for certain element, but for all its parents.
    If there’s a span inside your item, clicks on span won’t pass .className check.
    Maybe, this could be solved using event.currentTarget instead of event.target, but I’m not 100% sure.

  • riclf

    You’ve made some good points. But it does raise the question, why. Each example is an exercise in successfully avoiding jQuery. But each example is less readable, more typing, less intuitive. But, bravo, it can be done!

    • Sir Roft

      Because removing an entire dependency works wonders for reducing the size of your site, and in turn improves performance.

      • riclf

        I don’t want to be argumentative so please see my comment just as a conversation- It seems to me that today “size” of a site refers to old parameters. Performance is the deal today. Yes in days past size might have reflected performance but performance is the parameter of issue. Maybe you are right that jQuery takes a toll on performance. I have not seen that in what I have done but I will say, you may well be right, and performance does matter. That is what the user/visitor experiences. Thanks.

        Raymond, yes, I am intrigued to become more familiar with fetch(). That is pretty cool. I’ll give it the time.

        • Patrick Housley

          Unfortunately, size of your site’s dependencies still matters today. Not everyone in the world has Google Fiber and mobile browsing has a huge user base. Of course there are techs out that are making this obsolete like HTTP2 and 4G. But still, not everyone in the world has access to the techs. That said, jQuery minified is 87kb (31kb compressed). That is not a lot and not enough for me to worry about but it is still larger than 0kb.

        • I didn’t take it that way – don’t worry. 🙂

        • Sir Roft

          ???

    • “Beauty is in the ey of the beholder.” 😉 Seriously – you saw it is less readable, but that may be because it is new to you. To me, it i much more readable, and I find the Fetch() syntax especially to be more direct then $.ajax.

      More typing – if I can save dependancies by typing document.querySelector vs $, then I’m fine with that. Heck, I could alias the function to $ if I want to.

      I think @sirroft:disqus ‘s reply covers it well too.

      • So I think the fetch syntax is pretty sweet, but looking at CanIUse it looks like roughly 80% of folks in the US (less worldwide) have browsers compatible with fetch…that seems like a pretty big downside to me.

        • I would say it depends on your audience. A small polyfill could help too (multiple exist).

    • MaxSchumacher

      if you go to jsperf and compare native implementations with Jquery it is shocking how much slower jQuery often is. I agree with the verbosity notion though.

  • Chris Jaynes

    I generally don’t use Jquery unless I need it for something other than this simple stuff. But despite what 8M people say, it is still a really convenient and practical way to manipulate the DOM. I see no reason to just give up on a decent library for no reason.

    The main Jquery features I use in just about every app of a reasonable size are not really trivial to do with the bare DOM APIs:

    – Scoped selectors via `find` (great for coding modular components)

    – Build html with template literals, and then easily get Jquery/DOM elements via `$`, `append`, `html`

    – Easy class manipulation: `hasClass`, `addClass`, `removeClass`, `toggleClass`

    – In addition to `prev`, `next` and `find`, other traversal functions like `parent`, `children`, `closest`.

    – Easy manipulation of multiple elements without having to loop over them

    – Easy manipulation of CSS properties on elements using `css` (best if used sparingly, but still often useful in dynamic situations where classes aren’t feasible)

    – Jquery specific pseudo-selectors like `:visible`

    On the flipside, I definitely don’t use `ajax` or `animate` anymore. We have better ways of doing those things, now. It’d be nice if there was a build of Jquery available in npm that only includes the DOM manipulation bits.

    • Braj Mohan

      You know, as always there is a JS package for this.

      https://www.npmjs.com/package/jquery-lite

      There is one another package which you don’t need to build yourself and includes everything but Ajax. I can’t recall the name right now though.

    • Patrick Housley
    • Daniel Flower

      Some of those are easy now.

      – Scoped selectors via `find` (great for coding modular components)
      – Build html with template literals, and then easily get Jquery/DOM elements via `$`, `append`, `html`
      – Easy class manipulation: `hasClass`, `addClass`, `removeClass`, `toggleClass`
      – Easy manipulation of CSS properties on elements using `css`

      A nice trick is to bind document.querySelector to a variable, and then use it like jquery’s $. But you can bind it so it’s scoped to a component too, and then manipulate classes and styles and create elements etc.


      let myComponent = document.getElementById('my-component');
      let $ = myComponent.querySelector.bind(myComponent);
      $('div').style.color = 'red'; // only the first div scoped inside the component will be affected

      $('.something-else').classList.toggle('some-class');

      let newDiv = myComponent.appendChild(document.createElement('div'));
      newDiv. innerHTML = `This page title is <b>${document.title}</b>`;

      Working on multiple elements is a bit more code:


      let $$ = myComponent.querySelectorAll.bind(myComponent);
      $$('h2').forEach(h => h.style.color = 'red');

      Always nice if you can reduce dependencies.

  • andrewstrader

    I agree: It’s fine to use jQuery (or any third-party library), but it’s best to reserve the option not to use it, like when it’s inconvenient or adds unnecessary complexity.

    And although I agree that jQuery makes DOM traversal much easier than `document.querySelectorAll()`, I’d suggest `document.evaluate()`, which is even more powerful than jQuery because you can use any XPath expression: https://developer.mozilla.org/en-US/docs/Introduction_to_using_XPath_in_JavaScript

    • That’s pretty interesting – I know of XPath and have used document.evaluate in the pas, but I had forgotten it could be used with the DOM. Thanks for reminding me.

  • jw kim

    jQuery is hard to recall

  • Adam

    I’m inclined to ignore these articles until they show up in JavaScript weekly. Now that this one is out in front of a broad audience that likely includes the people I’ll be interviewing over the next few weeks, I’m obliged to point out that whether one uses jQuery or not is irrelevant. Binding data and events by hand is an imperative style of development that’s inherently buggier, more verbose, and much harder to reason about than its declarative alternatives like React, Vue, or the dozens of other modern frameworks. I find the reticence of many front-end developers to learn these frameworks confusing, and the degree to which they’re willing to torture themselves with native DOM methods even more so. Why learn how to ride a horse without a saddle when there are cars?

    • Let me suggest an alternative to your question – why use the car when you’re just walking down the hallway? Don’t get me wrong – I love Angular, and Vue even more so. And Vue is *really* lightweight and works great for smaller things. But if you don’t need a framework, it’s overkill to add it.

      I’d also ask – why do you think this article promotes, or suggests perhaps, something against learning frameworks? Rather it was very specifically – here are things jQuery was really good at and here are ways to do it with vanilla JS. That has zero to do with whether or not you would use a framework. It’s just general knowledge about JS and the web platform in general. Much like how knowing how your car works is useful, even if you don’t necessarily tinker in it.

      • Adam

        Beware of conflating JavaScript with the DOM. JavaScript has its own warts. But the DOM is an unredeemable beast of bad architecture and browser inconsistencies. The suggestion to not use frameworks is implicit in the screenshot at the top of this article. It points to a consensus that jQuery is a dirty word *and* implies that the naked DOM its replacement. The bone I’m picking is over why exactly jQuery deserves to be a dirty word. Is it really because it’s redundant? Or is it something else?

        I readily concede that if you don’t need dynamic data binding, route binding, form validation, or state management, then you don’t need a framework. The rub is, if you really don’t need those things, can you really make do with node lists that have no array-like methods; event bindings that can’t do delegation; a fetch API with broken error handling; and (after all these years) persistent inconsistencies in browser implementations? What a novel project to lack such requirements! But when articles like this come out, the conclusion reached by Treehouse and coding bootcamps is a resounding, “Yes! Jump out of your jQuery plane! The DOM will catch you!” Their students, who are keenly aware that employers aren’t looking for jQuery anymore, come to their colleagues asking for help with their mountains of many-nested conditional blocks and inscrutable side effects. They expect kudos for not using jQuery whilst simultaneously asking why their code is broken. What a depressing activity in 2017 to untangle such code.

        The world is full of WordPress developers who are coasting on their ability to weave together layers of themes and plugins. Peter Paul Koch tells them their reluctance to learn frameworks is justified. OK, fine. Pick the right tool for the right job. A framework isn’t always the right tool. But let’s not push people out of their jQuery planes under the pretext that learning the DOM will make them better pilots.

        • So to be clear – my statements *explicitly* saying that I wasn’t telling people not to use jQuery wasn’t clear enough? The entire first section made it incredibly clear that if jQuery is working for you that I absolutely wouldn’t tell you to stop using it. Yes I point out folks are suggesting to move away from it, but I went out of my way to let people know that wasn’t the case here. If folks can’t read the *beginning* of an article, then I don’t know what else to suggest.

          As “broken” as the DOM may be – developers can still work with it w/o needing to resort to a framework.

          As I said, very clearly, these were the methods I were using instead of jQuery. It wasn’t against jQuery. It wasn’t against frameworks. If you want to read that into it, then that is your right.

        • MaxSchumacher

          the notion of extreme inconsistency in the DOM is outdated. If you look at feature coverage (both in terms of the ES spec and in regard to Web APIs) per browser version and then look at active browsers you will find that things have gotten a lot better.

          The jQuery to native knowledge is valuable for people like me: I have inherited a legacy system in production use – I can make gradual changes (such as replacing DOM selector methods to improve performance and decrease dependencies), but just throwing React at an existing codebase is not going to cut it. Greenfield projects are rare.

          • Adam

            @MaxSchumacher:disqus, I can’t say it enough times: The DOM has nothing to do with ECMA. The DOM and the JavaScript language are completely different things. Browser support for the JavaScript language is so predictable that you can use a Babel plug-in to transform code to target specific versions. The DOM, however, is still a big mess:

            https://www.quirksmode.org/dom/events/

            I’m not clear on what value you’re getting out of replacing jQuery code with direct DOM manipulation, but I’ve been bitten enough times to know that it’s still not consistent enough for me to advise newcomers to do so. If you depend on jQuery and use a common CDN version, your users likely already have it cached and the overhead is negligible. As to frameworks, a project needn’t be a green field to introduce a one if it’s complex enough to warrant it.

    • Brook Monroe

      Okay, what the hell. Why don’t I use those frameworks? NO STANDARDS. While the implementations may not be perfect in all browsers, at least JavaScript and the DOM have standards and move forward in a rational way through community input and debate.If Angular => Angular2 isn’t a cautionary tale, I don’t know what is.

  • Few notes on the various sections …

    Page Load:
    Depending on when you need to start working with the DOM, you may not need “load” at all. Place your script tag at the bottom of the page, the DOM is already loaded, and you can start working with it. No need to delay the user/rendering if you don’t have to. With ES6 that looks something like …

    class CatPhotos {

    }
    let app = new CatPhotos();

    Event Listeners:
    Elements can even emit their own events, and carry their own data along with them using Custom Events …

    let elem = document.querySelector( ‘#cat’ );
    elem.dispatchEvent( new CustomEvent( ‘cat_event’, detail: {
    funny: true
    } ) );

    Getting DOM Elements:
    CSS selectors let you get pretty fancy these days. You can even use selectors like “:not()”. Also, elements themselves allow you to use “.querySelector()” which looks for matching elements just within themselves …

    let elem = document.querySelector( ‘.catz’ );
    let funny = elem.querySelectorAll( ‘.funny’ );

    Reading and Writing DOM Elements:
    I often use templates in my apps. There’s even a “template” tag for this now, but I’m not a big fan of the syntax. If you’re not already using a framework for this, you can clone nodes …

    let template = document.querySelector( ‘.cat.template’ );
    let photo = template.cloneNode( false );
    photo.classList.remove( ‘template’ );
    photo.classList.add( ‘photo’ );
    document.body.appendChild( photo );

    Class Manipulation:
    This came up in the comments, but elements have a “classList” property (see previous example) which makes working with the applied styles much easier. You can even “.toggle()” and “.replace()”.

    Vanilla JS has come a long way since jQuery was introduced. Knowing the DOM API and how it works is invaluable – even if you are using a framework, so that you understand what they are doing under the covers.

    • Thanks Kevin, all great points.

      templates: In general – if I find myself making a lot of dynamic HTML from JS I find myself leaning on a framework for that. Since finding Vue recently, that’s definitely where I’d go.

  • okame
  • Wenfeng LI

    In jquery, even if a single node must be wrapped into an array, this style always feels illogical, as an alternative to jquery, you can consider using skylark-utils

    skylark-utils is an universal dom utility library, supports the following three types of APIs:
    1.node level api
    Directly specify the target dom node for dom operation, simple and intuitive
    2.vme api
    use a vme object wrapping single dom node. support chain-call,so suitable for multiple operations on the same dom node
    3.query api
    use a query object wrapping node list. suitable for multiple operations on the same dom node

    please see https://github.com/skylarkjs/skylark-utils

    In addition, the optional skylark-jquery library is based on the skylark-utils/query module, providing APIs that are fully compatible with jquery,and the codes is simpler and more productive, JQuery plugins and applications can run directly on skylark without jquery.

    please see https://github.com/skylarkjs/skylark-jquery

  • Brian Voelker

    One of the things that was holding us back from completely disconnecting from jquery was a good select drop down. So i built one myself. If this is a wall for anyone else give http://slimselectjs.com a shot.

  • Luca Rainone

    “Writing HTML inside a DOM element is also simple – instead of $something.html(string), you would use $something.innerHTML=string”

    try to insert javascript stuff inside your string. ie: alert(“hello”). The behavior is different here.

    jQuery simplify (or has simplified) our life more than we think. Maybe not even in the right way, but it has solved a lot of problem that we don’t knew to have

    • Why would I try to insert JS that way though? 🙂 If I were trying to dynamically load JS, I’d probably append a script element to the DOM.

      “jQuery simplify (or has simplified) our life more than we think.” Absolutely. And I hope folks are picking up on the whole vibe of my article *not* being an attack on jQuery. It was, and is, an incredible tool.

      • Luca Rainone

        “Why would I try to insert JS that way though?”
        Ajax call with html result for example. But anyway it’s just an example (if the jQuery team has supported that, maybe it was a widespread problem 🙂 I have reported you because you wrote this example explicity without mention this feature.

        • Well, imo, “inserting HTML” isn’t the same as “inserting JS”. 🙂 But ok – good point.

  • PersonaBLAH

    Oh boy, another one of these articles. No thanks.

    • If you had spent as much time reading as you had commenting, you would have seen that this *wasn’t* yet another of those articles. 😉

  • Frédéric Hewitt

    I tried to used Fetch API, until I needed to set a shorter timeout and discovered that fetch is far behind the jQuery ajax method in features. I tried to use native Promises until I discovered that they don’t support any form of finally/always method, or deferred object. I tried to implement the native event binding, until I discovered that outside of trivial selector cases, it’s add code complexity that hinder code understanding.

    Yes, you can re-implement these features, but that defeat the original purpose of avoiding a library if you need to add a ton of utility functions that you copy/paste between your projects.

    The truth is: native solutions are still a really poor replacement for a mature, solid & efficient library.

  • Erik Anderson

    If it works for you great! And I readily admit removing jQuery from some of my simple pages where its functionality can be easily substituted with vanilla JavaScript.

    But I’m beginning to think a lot of people are forgetting why jQuery came about it in the first place; it was created to abstract away the inconsistencies of each browser’s implementation of JavaScript and the DOM thereby allowing developers to focus on the application instead of quirks and idiosyncrasies.

    A lot has changed in the intervening years but it (and its brethren) still serve as nice safety nets.