Comment by flomo

19 hours ago

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.

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.

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

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.

      6 replies →

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

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

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

      1 reply →

  • 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);

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.