JQuery Footguns?

github logo ・1 min read

I've been tasked with building a small application over the next two weeks, and must use JQuery to do so. Left to my own devices I'd probably just keep it vanilla, but leaving me to my own devices is not generally recommended.

I've never used JQuery for anything larger than 200 lines, and that was circa 2012. Is there anything I should watch out for? Any seemingly innocuous patterns that create more problems than they solve?

A quick glance through the documentation suggests I can just sort of use it as a shorthand for some common operations like grabbing DOM nodes and performing AJAX. Should I just be building my application in much the same manner as I would be otherwise, just more concisely, or are there ways to leverage the library even more powerfully that I'm missing?

Thanks!

Photo by Ben White on Unsplash

twitter logo DISCUSS (33)
markdown guide
 

Some people preach against jquery but jquery is still a big handy.

  • It doesn't need a polyfill, so it is one problem less. Unless you are using a modern version of jquery. Personally, I don't see the reason to jump over Jquery 3.x

  • Missing object

$("#textbox").html("hi"); // will not crash if textbox doesn't exist
document.getElementById("textbox").innerhtml="hi"; // will crash if textbox exist.

It is because $() always returns a jquery object, not a DOM object.

  • cache
$("#textbox").html("hi")
$("#textbox").html("hi again")

This code is calling to the DOM twice and it's searching for the object textbox. In this case, the object must be cached as follow

let obj=$("#textbox");
obj.html("hi");
obj.html("hi again");
 

One thing I just remembered:

I used to use a convention to distinguish jQuery objects from regular variables, stuff like var $textBox = $("#textBox"). Don't know if it still makese sense but it did at the time :D

 

I've also been doing this for a long time. It's very handy.

 

Oh, I like that a lot. Most likely stealing this.

 

Thanks for this! Great point that you always get back a JQuery object - those extra null checks are clunky otherwise.

 

Short list of things that come to mind:

  • Take the time to compare the jQuery AJAX stuff to a minimal open-coded implementation covering exactly what you need. You'll often find that just coding it yourself will both save space (if you can use a custom build of jQuery with the AJAX stuff stripped out) and save time. It also makes migrating to jQuery alternatives a bit easier. The same goes for the animation stuff (by the way, if you need animations more powerful than the basic canned stuff jQuery provides, I'd suggest looking into Anime.js, it's fast, tiny, and very powerful).
  • Cache your jQuery objects. For really trivial cases, performance isn't too bad, but the moment you start getting complex selectors (or using class selectors on older browsers), you will see some serious performance issues.
  • Use .prop() instead of .attr() whenever you can. It's significantly more efficient, and it ensures that the browser's internal understanding of the DOM state gets updated properly. Even further, use specific jQuery methods for the properties that have them (value for example, which has the .val() method).
  • Use event delegation whenever possible. This will help keep the number of event listeners down, which in turn helps a lot with app performance on many devices. Using event delegation also means you don't have to care if the specific element you want to match on even exists in the DOM when you add the event.
  • Make sure to keep track of which variables refer to jQuery objects. Most people just prefix such variables with $ as a reminder. This is more a case of writing readable code than actual performance.
  • Remember that jQuery always returns a valid object, even if your selector matches nothing (in that case, you get an empty jQuery object). This actually does have practical uses, but it also means that things that would crash if done through VanillaJS methods will silently fail to do what they say when using jQuery.
  • Keep in mind that many jQuery methods apply to all matched elements. In a lot of cases, there's no point in manually looping over the returned results of a jQuery selector call because you can simply update everything with one function call (which will be faster, especially for very large numbers of matched nodes).
  • Pretty much every jQuery method which mutates the state of the DOM returns the jQuery object it was called on. As such, you can often chain a series of methods that modify your selected element. While this doesn't make things any faster (at least, not measurably on any sane JS engine), it does make the JS code a bit smaller (each method you chain this way saves 2 bytes of code after running the source through a minifier).
  • Certain jQuery methods (most notably .addClass() and .removeClass()) are functionally idempotent and declarative. Put simply, $('#foo').removeClass('bar') will never return an error, and will always ensure that the element with ID foo does not have the class bar. As a result, stuff like this just wastes time:
if (!$('#foo').hasClass('bar')) {
    $('#foo').removeClass('bar')
}

Also, you might want to see if you can convince the decision maker to use Cash instead, it's like jQuery, just significantly smaller and significantly faster (and it unfortunately doesn't work with Bootstrap or some popular jQuery plugins like select2).

 

Wow, this is incredible, thanks so much for taking the time. Every point here is useful!

just coding it yourself will both save space ... and save time

This is my instinct as well, so I'm using the opportunity to specifically leanr about JQuery - otherwise I expect it to feel frustrating at times.

Use .prop() instead of .attr() whenever you can.

Did not know this, thanks much.

you don't have to care if the specific element you want to match on even exists in the DOM when you add the event.

Hadn't thought of that, that's good to keep in mind.

methods will silently fail

How do you mitigate this? Explicit run-time checks?

there's no point in manually looping over the returned results

This is one I did "know" but still somehow didn't realize you could use this way. Awesome.

functionally idempotent and declarative

This I did not realize and am a huge fan of. Unless I'm missing it, I don't think the docs make this very clear, but thinking about it, of course that's how they work.

Had not heard of cash, I'll make the case! Looks pretty snazzily familiar, he may go for it.

 

Use .prop() instead of .attr() whenever you can.

Did not know this, thanks much.

Yeah, this one could stand to be covered more in a lot of jQuery tutorials. It's a regular source of subtle bugs as well as performance issues in code written by people who aren't familiar with jQuery.

methods will silently fail

How do you mitigate this? Explicit run-time checks?

I don't know what the norm is in big jQuery projects. For any of my stuff, I just make certain that things are strictly ordered such that this never happens (and when I can't ensure strict ordering, I use Arrive). FWIW, if you want to check, the most reliable approach is to check the length property of the returned jQuery object (if it's zero, nothing matched).

functionally idempotent and declarative

This I did not realize and am a huge fan of. Unless I'm missing it, I don't think the docs make this very clear, but thinking about it, of course that's how they work.

Yeah, the docs really don't make it clear, and while it kind of makes sense, it is inconsistent with how most programmers assume most things behave by default.

Had not heard of cash, I'll make the case! Looks pretty snazzily familiar, he may go for it.

Best time to go for it is when starting out. It's pretty easy to switch to jQuery after the fact if you need to, but not so much going the other way.

most reliable approach is to check the length property of the returned jQuery object

That works for me - I don't think this project will qualify as "big". 1-2k lines of JS total. Hadn't seen arrive, that looks handy, but I think I'm going to see how I fare without it at first.

Seriously, this has been a huge help. You've boosted my confidence going in to this project, I feel a little less lost. Thanks again!

 

Something that I learned from jQuery was the event delegation and is easy to implement:

$('#parent').on('click', '#dynamicCreatedChild', e => {})

A good thing to remember too is the data() method used in jQuery not set values in data attr but can read it.

Using the data() method to update data does not affect attributes in the DOM. To set a data-* attribute value, use attr

docs

And use a internal system of cache, so if you update the attr data you need to retrieve with attr().

Since jQuery 1.4.3, data-* attributes are used to initialize jQuery data. An element's data-* attributes are retrieved the first time the data() method is invoked upon it, and then are no longer accessed or mutated (all values are stored internally by jQuery).

docs

 

Beautiful, thank you for pointing this out!

 

I haven't done much with jQuery event delegation, but I am guessing you need to be careful to turn off the listener if you need garbage collection?

 

hey, non related question but are you a freelancer or a full-time worker?

 

Neither, really. This represents my first significant freelance situation, and I'm currently working towards finding myself a full-time role. I work in a non-tech capacity currently.

 

this is awesome then. i hope next time you would be able to find a client that does not demand jquery haha. in the time being you did not tell us what you are trying to build or do. could you share something on that?

I'm hesitant to share the spec in any sort of detail, maybe I'll come back and recap after completing it! It's a small-scope full-stack application: Node/RethinkDB/JQuery/MaterialzeCSS.

 

awesome, man! congratulations! what are your skills? do you do both back end and front end? what is your stack? glad to hear. thanks.

 
 

Aha, I've seen this but never knew what it was called. Thanks for the link, great read!

 

Don't know if you know but jQuery has promises/deferreds/futures. You can do neat things with them :)

Should they be used in lieu of es6 promises?

They came before, I think they've inspired them. I'm not sure how compatible they are. Methods like $.get() return such deferreds, but I haven't tried to mix the two :D

A cursory glance suggests they aren't really compatible. I'll have to play with it, I've only used the newfangled jazz.

 

Actually jQeury is not so bad. Especially the UI version of it its quite modular and you can build big applications with it. Just don't couple UI logic with business logic.

 

Ah, thanks for the link! I'd never heard of that, looks like a huge amount of work that can be saved there. I definitely don't have any sort of stigma against JQuery, just never got around to using it much.

Just don't couple UI logic with business logic.

Seems like good advice in general, JQuery aside. Thanks again!

 

I would suggest to use a template engine like handlebarsjs.com/ for anything related to building HTML

Good tip! Manually constructing DOM nodes gets old fast.

 

Saw this yesterday, you may want to consider it over jQuery blissfuljs.com/

Haven't worked with it so far

 

Ooh, this does look really nice. For this project I've gotta use JQuery, not my rule, but I'm definitely going to explore this for something else. Thank you!

 

Depending on the browsers the app is going to run, you may consider not use jquery at all: youmightnotneedjquery.com/

 

Not up to me for this project, but thanks! I agree, I generally steer clear of any dependency at all if I can.

Classic DEV Post from Jul 17

What are you (still) not interested in learning?

I'm interested in hearing not only what you are *not* interested in learning. But if you answered the question last time, has anything changed?

Ben Lovy profile image
Hobbyist. Learning Rust, JavaScript, C++. He/him.

👋 Hey dev.to reader.

Do you prefer sans serif over serif?

You can change your font preferences in the "misc" section of your settings. ❤️