Use $ & $$ Instead of document.querySelector/All in JavaScript without jQuery
Ahmad Awais ⚡️

Ahmad Awais ⚡️ @ahmadawais

About: VP of DevRel RapidAPI ❯ Award-winning Web Developer NodeCLI.com ❯ Google Dev Expert Web tech ❯ 2x GitHub Stars Award ❯ WordPress Core Dev ❯ TEDx Speaker ❯ "awesome example for devs" — Satya Nadella

Location:
San Francisco Bay Area
Joined:
Feb 19, 2017

Use $ & $$ Instead of document.querySelector/All in JavaScript without jQuery

Publish Date: Apr 2 '19
231 32

I started writing JavaScript code because of WordPress back in 2007. JavaScript was not what it is today. It would behave differently on different browsers and there were too many hacks around basic stuff to keep up with all the changes all the time.

So, many of us decided to rely on jQuery — a simple JavaScript library with one single form of syntax that just worked everywhere in all the browsers.

Fast forward to 2019, as a full-time JavaScript Developer Advocate — I advocate modern JavaScript. Because it's super awesome. Though at times I miss the simplicity of jQuery in things like you'd just need a $ sign to pick up an element and do stuff to it.

Now with JavaScript, I find myself doing document.querySelector multiple times in an application. Well, guess what, there's an easy way to just bind that $ sign to your document's document.querySelector.

gif

Here's how you go about it.



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


Enter fullscreen mode Exit fullscreen mode

Now you can use the following:




// Change the background color of a class.
$('.class').style.background="#BADA55";

// Change the inner HTML of an ID.
$('#id').innerHTML="<span>Cool beans!</span>";

// Select all images on the webpage.
$$('img')

// Print the image addresses for all the images on a webpage.
$$('img').forEach(img => console.log(img.src))


Enter fullscreen mode Exit fullscreen mode

Use your code for good and have fun! :)
Peace! ✌️

Comments 32 total

  • Cedric W
    Cedric WApr 2, 2019

    This looks cool.

    with you example could you still do this though?

    const username = 'johndoe'
    const age = 32
    
    console.log(`The name of the user is ${username} and his age is ${age}.`)
    
    
    • Cedric W
      Cedric WApr 2, 2019

      now that I think about it, I guess this would be a scope issue

    • JavaScript Joel
      JavaScript JoelApr 2, 2019

      Yep, it would look like this:

      const username = 'johndoe'
      const age = 32
      
      console.log(`The name of the user is ${username} and his age is ${age}.`)
      console.log(`Element is: ${$('#article-reaction-actions')}.`)
      

      The outer $ in the template string will start the evaluation. the $ inside of ${} will refer to the global $.

      Hope this clears it up!

  • Jason R Tibbetts
    Jason R TibbettsApr 2, 2019

    👍

    This is an awesome tip!

  • sesay
    sesayApr 2, 2019

    cool :)

  • Perlat Kociaj
    Perlat KociajApr 2, 2019

    Great tip Ahmad.
    Thank you for sharing.

  • Den McHenry
    Den McHenryApr 2, 2019

    If you work on a distributed team or with legacy code, and considering the potential for conflict (mostly with jQuery but some other libraries, and any code where someone may have used $ for document.getElementById(), as used to be common), I think it'd be a good idea to use trivially longer identifiers like this:

    const $q = document.querySelector.bind(document);
    const $qa = document.querySelectorAll.bind(document);
    
    Enter fullscreen mode Exit fullscreen mode

    This is similar to a common method of using jQuery in noConflict mode, e.g.:

    const $j = jQuery.noConflict();
    
    Enter fullscreen mode Exit fullscreen mode

    It could be judiciously extended for similar methods if you use them often enough.

  • Kevin McKenna
    Kevin McKennaApr 2, 2019

    I'm interested in your thoughts a little deeper on this:

    "I advocate modern JavaScript. Because it's super awesome." - Why would you advocate modern JavaScript ahead of jQuery beyond it being awesome. What do you feel is the main selling point is that can't be accomplished in other ways?

  • Eljay-Adobe
    Eljay-AdobeApr 2, 2019

    Ugh, I had to replace jQuery in our app with a work-a-like jQuery that I wrote. It was THE WORST, because I had to learn jQuery, then I learned all about jQuery's internals, then I had to figure out the subset of jQuery that we depended upon that did not have all the internal state that jQuery had (did everyone know jQuery has a ton of internal state?), then I had to put up with all the upset other JavaScript developers who complained about my not-jQuery not behaving like jQuery when they added new code that relied on jQuery-isms that I hadn't implemented because the prior versions of the code hadn't relied upon those facilities (so I didn't bother to implement them).

    What a royal pain in the keester.

    I wish I had the foresight to just do your $ and $$ bindings and tell all the other devs to suck it up. Your solution is GENIUS.

    • Tommy Williams
      Tommy WilliamsJan 22, 2020

      Could you explain the purpose of creating your jQuery-like library? I don't understand what you gained, compared to just continuing to use jQuery.

      • Eljay-Adobe
        Eljay-AdobeJan 23, 2020

        The lawyers at my company would not allow us to ship or use third party libraries.

  • Vincent Milum Jr
    Vincent Milum JrApr 2, 2019

    Things like this are AMAZING so long as you don't have to support any legacy systems. It's nice to see that the feature set built by jQuery is essentially built into the browser directly now, and a simple wrapper gives us similar functionality.

  • Tgwizman
    TgwizmanApr 2, 2019

    I have been using
    $ = function(e) {return document.getElementById(e)}
    for years. Now I started using
    $ = function(e) {
    switch (e[0]) {
    case '.': return document.getElementsByClassName(e.substring(1, e.length)); break;
    case '#': return document.getElementById(e.substring(1, e.length)); break;
    default: return document.getElementsByTagName(e);
    }
    }

  • Niko Solihin
    Niko SolihinApr 3, 2019

    Great tip Ahmad.

    Also, in Chrome dev tools, $ and $$ are already bound by default to querySelector and querySelectorAll respectively.

  • vaaski
    vaaskiApr 3, 2019

    one question.
    i always did it like this:

    const $ = s => document.querySelector(s)
    
    Enter fullscreen mode Exit fullscreen mode

    what are the differences?

  • Itachi Uchiha
    Itachi UchihaApr 3, 2019

    I'm using document.querySelectorAll for all elements. Even for id elements.

    Actually, you can use querySelectorAll for id elements. It will work for one element. There must not be multiple elements in a document that have the same id value.

    const $ = document.querySelectorAll.bind(document)
    
    const testEl = $("#test")
    
    testEl.forEach(el => {
        console.log(el)
    })
    
  • Hendra Susanto
    Hendra SusantoApr 3, 2019

    Instead of using 2 different constants, what about checking the length of the selector first like this:

    const $ = s => document.querySelectorAll.bind(document)(s).length > 1 ? document.querySelectorAll.bind(document)(s) : document.querySelector.bind(document)(s);
    

    or something like that to make it much more similar to jQuery $?

    • Thibaut Allender
      Thibaut AllenderApr 4, 2019

      From a performance perspective, that would double the amount of work for every selector. Going from jQuery to Vanilla JS is mostly about being more performant, so that looks like an anti-pattern.

      • Ahmad Awais ⚡️
        Ahmad Awais ⚡️Apr 6, 2019

        Can you explain how would that be the double amount of work? It's just one extra call.

  • intrnl
    intrnlApr 3, 2019

    Isn't $ and $$ already binded to querySelector and querySelectorAll from the start though? Which means that binding it again seems unnecessary.

    or I suppose the default binding is recent..?

  • Andrea Giammarchi
    Andrea GiammarchiApr 3, 2019

    Chrome has its own Console Utilities API Reference where $ and $$ are offered regardless, among other utils, unless the global scope has been overwritten with something else (i.e. jQuery).

    The API for $ and $$ is more jQuery-ish, and if you'd like to reproduce it its like:

    const $ = (css, parent = document) => parent.querySelector(css);
    const $$ = (css, parent = document) => parent.querySelectorAll(css);
    
    Enter fullscreen mode Exit fullscreen mode

    To avoid needing polyfills for forEach or Symbol.iterator on NodeList collections, you can also go ahead and convert the static collection as Array so that all methods, including filter and map can be used.

    const $$ = (css, parent = document) =>
      Array.from(parent.querySelectorAll(css));
    
    Enter fullscreen mode Exit fullscreen mode

    If you don't have Array.from in your debug session, or you are after a thin layer that works across browsers, you can go for the following:

    function $(css, parent) {
      return (parent || document).querySelector(css);
    }
    
    function $$(css, parent) {
      var nodes = (parent || document).querySelectorAll(css);
      return Array.prototype.slice.call(nodes, 0);
    }
    
    Enter fullscreen mode Exit fullscreen mode
    • Stebeus
      StebeusNov 23, 2025

      Sorry for replying an old post, but how does this method of aliasing these two query selectors compares to the OP's post for development, especially for the parent parameter?

  • Massimo Artizzu
    Massimo ArtizzuApr 3, 2019

    Watch out that querySelectorAll returns a NodeList object, whereas $$ in the console returns an array.

    So if you're used to call all the sweet array prototype methods, you might fall short!

    Solution: wrap everything in Array.from.

  • florent giraud
    florent giraudApr 3, 2019

    I already did this in some projects that's cool !
    But one point to take care is about performance.
    Because querySelector do a lot more jobs thant getElementsById or class so if you dont care about perf you solution is perfect

  • Lazar Ljubenović
    Lazar LjubenovićApr 4, 2019

    I made a module exactly for these purposes just recently: gimme back my dollars! It has a very distinctive name.

  • Marek
    MarekOct 8, 2019
    const $ = (selector, base = document) => {
       let elements = base.querySelectorAll(selector);
       return (elements.length == 1) ? elements[0] : elements;
    }
    
  • Tormod Vold Mikkelsen
    Tormod Vold MikkelsenOct 16, 2019

    This is my take on it. It checks whether I'm asking for an id or not, and if I am, it returns just that one element. If not, the NodeList is made into an array.

    const $ = (q, d = document) => 
      /#\S+$/.test(q)                       // check if query asks for ID
      ? d.querySelector.bind(d)(q)          // if so, return one element
      : [...d.querySelectorAll.bind(d)(q)]  // else, return all elements in an array.
    
  • Roso Sasongko
    Roso SasongkoAug 5, 2020

    ...and finally we returned to jQuery

Add comment