Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Whenever HTMX comes up here, I always think "isn't that just some gobbledy-gook which replaces about 3 lines of imperative jquery?"

Anyway, jQuery always did the job, use it forever if it solves your problems.



yes: htmx grew out of intercooler.js, which was based on jquery and inspired by the jQuery.load() method:

https://api.jquery.com/load/

which I found while doing some performance work at one point. intercooler.js started as a custom function that hooked .load() in based on custom attributes (a trick I learned from angular 1.x)

deeply respect and love jquery


Are you Carson?


yep


These days I’ve moved to native JS, but hot damn the $() selector interface was elegant and minimal vs document.getElement[s]by[attribute)].

While presumably jquery is slower than native selectors, maybe that could be pre-computed away.


In case you missed them: check out querySelector and querySelectorAll. They are closer to what the jQuery selector system does, and I think they were inspired by it.

If the verbosity bothers you, you can always define an utility function with a short name (although I'm not personally a fan of this kind of things).

https://developer.mozilla.org/docs/Web/API/Document/querySel...

https://developer.mozilla.org/docs/Web/API/Document/querySel...

https://developer.mozilla.org/docs/Web/API/Element/querySele...

https://developer.mozilla.org/docs/Web/API/Element/querySele...


body.qsa('.class').forEach(e=>): Yes, add qs() and Array.from(qsa()) aliases to the Node prototype, and .body to the window, and you’ve saved yourself thousands of keystrokes. Then you can get creative with Proxy if you want to, but I never saw the need.


Please don't mess with native prototypes though.


Agree if you've a library developer. If you're an app or website developer then it's your project. Everyone else should steer clear of adding to native prototypes, just so they are clean for the end user.


If you are an app or website developer, at least you won't break other's systems.

But you might still break stuff in your own projects. Imagine you extend a native prototype with a method, and later the native prototype starts having a method with the same name.

Newer libraries start using that new standard method.

You upgrade the libraries your website depends on, or add a dependency, and this new code happens to depend on that native prototype. Only you replaced it with your custom method, and that method likely doesn't have the exact same behavior. You broke that new code and fixing this might not be trivial because uses of your custom method are sprinkled everywhere in your code.

It only works if you ever works on projects that have zero dependencies, or dependencies you never update.

Or you could spare yourself the troubles and define a method that takes the node in parameter.

It's also a question of forming good habits: you could be working on your projects now, forming a habit of extending prototypes, but will you remember not to do this the day you write a library?

By the way, how can you be sure you won't move some of your app code to a library because you like your utility functions and would like to reuse them in another project of yours? And why not open source that shared code, so you can just install it with NPM? Bam, that stuff is a library now.


> You upgrade the libraries your website depends on, or add a dependency, and this new code happens to depend on that native prototype. Only you replaced it with your custom method, and that method likely doesn't have the exact same behavior. You broke that new code and fixing this might not be trivial because uses of your custom method are sprinkled everywhere in your code.

He was suggesting adding a prototype method, not replacing one. Unless the library your using is also adding prototypes, I can't think of an issue with this. Sure, if a new version of JS ends up using these names then things could break, but I'd bet this won't cause him a problem in actuality.


I'm discussing the "adding a prototype method" case and I'm explaining in my comment why this can be, in fact, an issue.


Thanks for the feedback. But you did recommend a method that takes the node as a parameter. What protects me from that method name being claimed by some library later in the exact same way?


We have a few oddly named methods for arrays and iterators because of the prototype and other libraries being very common.


Rules beget rules.


I used to use prototype (and sometimes scriptaculous)... Then came IE8 and broke the world on me.

For anyone that didn't know, IE8 implemented native JSON.(parse/stringify) methods, and the second parameter is a hydrator/dehydrator... however, if you added custom properties/methods to Array or Object prototypes, they would throw an error you couldn't catch in JS... so to work around, you'd have to load the JSON library and use it under a different name, in ALL your code, because the native implementation was locked/sealed and couldn't be masked out.

Second most annoying IE bug was 5.0.0 and the old/new api's for dealing with select elements. New worked, old broken, have fun.


The $ (and $$) selector functions live on in chrome/chromium devtools!


const $ = document.querySelector.bind(document);

const $$ = document.querySelectorAll.bind(document);


Very simple jquery implementation with all the easy apis:

  (function (global) {
    function $(selector, context = document) {
      let elements = [];

      if (typeof selector === "string") {
        elements = Array.from(context.querySelectorAll(selector));
      } else if (selector instanceof Element || selector === window || selector === document) {
        elements = [selector];
      } else if (selector instanceof NodeList || Array.isArray(selector)) {
        elements = Array.from(selector);
      } else if (typeof selector === "function") {
        // DOM ready
        if (document.readyState !== "loading") {
          selector();
        } else {
          document.addEventListener("DOMContentLoaded", selector);
        }
        return;
      }

      return new Dollar(elements);
    }

    class Dollar {
      constructor(elements) {
        this.elements = elements;
      }

      // Iterate
      each(callback) {
        this.elements.forEach((el, i) => callback.call(el, el, i));
        return this;
      }

      // Events
      on(event, handler, options) {
        return this.each(el => el.addEventListener(event, handler, options));
      }

      off(event, handler, options) {
        return this.each(el => el.removeEventListener(event, handler, options));
      }

      // Classes
      addClass(className) {
        return this.each(el => el.classList.add(...className.split(" ")));
      }

      removeClass(className) {
        return this.each(el => el.classList.remove(...className.split(" ")));
      }

      toggleClass(className) {
        return this.each(el => el.classList.toggle(className));
      }

      hasClass(className) {
        return this.elements[0]?.classList.contains(className) ?? false;
      }

      // Attributes
      attr(name, value) {
        if (value === undefined) {
          return this.elements[0]?.getAttribute(name);
        }
        return this.each(el => el.setAttribute(name, value));
      }

      removeAttr(name) {
        return this.each(el => el.removeAttribute(name));
      }

      // Content
      html(value) {
        if (value === undefined) {
          return this.elements[0]?.innerHTML;
        }
        return this.each(el => (el.innerHTML = value));
      }

      text(value) {
        if (value === undefined) {
          return this.elements[0]?.textContent;
        }
        return this.each(el => (el.textContent = value));
      }

      // DOM manipulation
      append(content) {
        return this.each(el => {
          if (content instanceof Element) {
            el.appendChild(content.cloneNode(true));
          } else {
            el.insertAdjacentHTML("beforeend", content);
          }
        });
      }

      remove() {
        return this.each(el => el.remove());
      }

      // Utilities
      get(index = 0) {
        return this.elements[index];
      }

      first() {
        return new Dollar(this.elements.slice(0, 1));
      }

      last() {
        return new Dollar(this.elements.slice(-1));
      }
    }

    global.$ = $;
  })(window);


jQuery but gets compiled out like svelte... Not a bad idea at all.


I hate to sound like a webdev stereotype but surely the parsing step of querySelector, which is cached, is not slow enough to warrant maintaining such a build step.


Some things you build not because they are necessary, but because you can.


const $ = document.querySelectorAll


I usually do

   const $ = (s, e = document) => e.querySelector(s)
and a similar one for $$.


Probably have to bind the this value.


The problem with jQuery is that, being imperative, it quickly becomes complex when you need to handle more than one thing because you need to cover imperatively all cases.


Yeah, that's the other HN koan about "You probably don't need React if..." But if you are using jquery/vanilla to shove state into your HTML, you probably actually do need something like react.


It's not really about state but dom updates.


After having some time to think about it, I've seen some really perverse DOM stuff in jquery. Like $(el).next().children(3) type stuff. So I think this stuff really fell-over when there was 'too much state' for the DOM.


I think if you want to go high-dom manipulation a la jQuery, and want some form of complex state, storing the state _on_ the DOM might make sense? Things like data attributes and such, but I also feel like that’s itching for something more like htmx or maybe svelte (I’ve not looked into either enough, so I may be completely off base).

I do agree with the notion that jQuery is easy to mishandle when logic grows beyond a pretty narrow (mostly stateless) scope. It’s fantastic up until that point, and incredibly easy to footgun beyond it.


Yeah, that's the thing, it might make sense in some simple 1-dimensional case, but beyond that it turns into spaghetti code (or a homebrew 'framework'). The big thing is that if you want to re-gigger some of the DOM, React is actually a lot nicer than jquery.


I’ll die on the “give me Vue over react any day” hill in that case, admittedly because I think React’s template/code mix is atrocious. I also _feel_ like React suffers from “why not do everything” syndrome, and that’s from a very naive perspective so grain or mountain of salt


Yeah, I'm not hyping react specifically, just point out that vanilla/jquery or even htmx only works well when the state is not that complicated.


Part of me feels the same way, and ~2015 me was full on SPA believer, but nowadays I sigh a little sigh of relief when I land on a site with the aesthetic markers of PHP and jQuery and not whatever Facebook Marketplace is made out of. Not saying I’d personally want to code in either of them, but I appreciate that they work (or fail) predictably, and usually don’t grind my browser tab to a halt. Maybe it’s because sites that used jQuery and survived, survived because they didn’t exceed a very low threshold of complexity.


Facebook is PHP ironically.


It was once upon a time, hence them moving to HHVM to interpret it, but it’s been all but replaced with a PHP spinoff named Hacklang now.


I think in 2026 Facebook is a conglomeration of a bunch of things... Definitely not just PHP anymore.


I pretty much use HTMX and vanilla JS to solve most problems, when I use Django at least. Keeps things simple and gives that SPA feel to the app too.


I'm mixed on HTMX for going a step beyond interactive forms, it's fine... but much more and I find HTMX and server-side Blazor for that matter really janky... button events with a round trip to the server can just feel wrong.

FWIW, also hated the old ASP.Net Webforms round trips (and omg massive event state on anything resembling dialup or less than a few mbps in the early 00's).

I just wish that React and other SPA devs kept some awareness of total payload sizes... I'm more tolerant than most, but you start crossing over/into MB of compressed JS, it's too much. Then that starts getting janky.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: