DEV Community

Cover image for JQuery Footguns?

JQuery Footguns?

Ben Lovy on July 02, 2019

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...
Collapse
 
rhymes profile image
rhymes

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

Collapse
 
peiche profile image
Paul

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

Collapse
 
deciduously profile image
Ben Lovy

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

Collapse
 
ahferroin7 profile image
Austin S. Hemmelgarn

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).

Collapse
 
deciduously profile image
Ben Lovy • Edited

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.

Collapse
 
ahferroin7 profile image
Austin S. Hemmelgarn

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.

Thread Thread
 
deciduously profile image
Ben Lovy

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!

Collapse
 
kip13 profile image
kip

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

Collapse
 
deciduously profile image
Ben Lovy

Beautiful, thank you for pointing this out!

Collapse
 
jsmccrumb profile image
Jacob McCrumb

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?

Collapse
 
marvelouswololo profile image
MarvelousWololo

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

Collapse
 
deciduously profile image
Ben Lovy

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.

Collapse
 
marvelouswololo profile image
MarvelousWololo

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?

Thread Thread
 
deciduously profile image
Ben Lovy

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.

Thread Thread
 
marvelouswololo profile image
MarvelousWololo

is it a spa?

Thread Thread
 
deciduously profile image
Ben Lovy

Yep, but again, very manageable scope. They're primarily an angular shop.

Collapse
 
marvelouswololo profile image
MarvelousWololo

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

Collapse
 
theodesp profile image
Theofanis Despoudis

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.

Collapse
 
deciduously profile image
Ben Lovy • Edited

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!

Collapse
 
theodesp profile image
Theofanis Despoudis

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

Thread Thread
 
deciduously profile image
Ben Lovy

Good tip! Manually constructing DOM nodes gets old fast.

Collapse
 
rhymes profile image
rhymes

The module pattern should help: learn.jquery.com/code-organization...

Collapse
 
deciduously profile image
Ben Lovy

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

Collapse
 
rhymes profile image
rhymes

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

Thread Thread
 
deciduously profile image
Ben Lovy

Should they be used in lieu of es6 promises?

Thread Thread
 
rhymes profile image
rhymes

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

Thread Thread
 
deciduously profile image
Ben Lovy

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

Collapse
 
edgarortegaramirez profile image
Edgar Ortega

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

Haven't worked with it so far

Collapse
 
deciduously profile image
Ben Lovy

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!

Collapse
 
marceloandrade profile image
Marcelo Andrade R.

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

Collapse
 
deciduously profile image
Ben Lovy

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

Collapse
 
deciduously profile image
Ben Lovy

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