Making a short alias for document.querySelectorAll

JavascriptNative Methods

Javascript Problem Overview


I'm going to be running document.querySelectorAll() a whole lot, and would like a shorthand alias for it.

var queryAll = document.querySelectorAll

queryAll('body')
TypeError: Illegal invocation

Doesn't work. Whereas:

document.querySelectorAll('body')

Still does. How can I make the alias work?

Javascript Solutions


Solution 1 - Javascript

This seems to work:

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

bind returns a new function which works identically to the querySelectorAll function, where the value of this inside the querySelectorAll method is bound to the document object.

The bind function is only supported in IE9+ (and all the other browsers) - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind


Update: In fact you could create shortcuts to a whole range of document methods like this:

const query = document.querySelector.bind(document);
const queryAll = document.querySelectorAll.bind(document);
const fromId = document.getElementById.bind(document);
const fromClass = document.getElementsByClassName.bind(document);
const fromTag = document.getElementsByTagName.bind(document);

Solution 2 - Javascript

A common answer is to use $ and $$ for querySelector and querySelectorAll. This alias mimics jQuery's one.

Example:

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

$('div').style.color = 'blue'
$$('div').forEach(div => div.style.background = 'orange')

div {
  margin: 2px;
}

<div>
  test
</div>
<section>
  <div>
    hello
  </div>
  <div>
    foo
  </div>
</section>

Solution 3 - Javascript

The JavaScript interpreter throws an error because querySelectorAll() should be invoked in document context.

The same error is thrown when you are trying to call console.log() aliased.

So you need to wrap it like this:

 function x(selector) {
     return document.querySelectorAll(selector);
 }

Solution 4 - Javascript

My solution covers the four following use cases:

  • document.querySelector(...)
  • document.querySelectorAll(...)
  • element.querySelector(...)
  • element.querySelectorAll(...)

The code:

let doc=document,
    qsa=(s,o=doc)=>o.querySelectorAll(s),
    qs=(s,o=doc)=>o.querySelector(s);

In terms of parameters, the selector s is required, but the container element object o is optional.

Usage:

  • qs("div"): Queries the whole document for the first div, returns that element
  • qsa("div"): Queries the whole document for all divs, returns a nodeList of all those elements
  • qs("div", myContainer): Queries just within the myContainer element for the first div, returns that element
  • qsa("div", myContainer): Queries just within the myContainer element for all divs, returns a nodeList of all those elements

To make the code slightly shorter (but not quite as efficient), the qs code could be written as follows:

let qs=(s,o=doc)=>qsa(s,o)[0];

The code above uses ES6 features (let, arrow functions and default parameter values). An ES5 equivalent is:

var doc=document,
    qsa=function(s,o){return(o||doc).querySelectorAll(s);},
    qs=function(s,o){return(o||doc).querySelector(s);};

or the equivalent shorter but less efficient ES5 version of qs:

var qs=function(s,o){return qsa(s,o)[0];};

Below is a working demo. To ensure it works on all browsers, it uses the ES5 version, but if you're going to use this idea, remember that the ES6 version is shorter:

var doc = document;

var qs=function(s,o){return(o||doc).querySelector(s);},
    qsa=function(s,o){return(o||doc).querySelectorAll(s);}

var show=function(s){doc.body.appendChild(doc.createElement("p")).innerHTML=s;}

//           ____demo____       _____long equivalent______      __check return___      _expect__ 
//          |            |     |                          |    |                 |    |         |

let one   = qs("div");      /* doc.querySelector   ("#one") */ show(one  .id    ); // "one"
let oneN  = qs("div",one);  /* one.querySelector   ("div")  */ show(oneN .id    ); // "oneNested"
let many  = qsa("div");     /* doc.querySelectorAll("div")  */ show(many .length); // 3
let manyN = qsa("div",one); /* one.querySelectorAll("div")  */ show(manyN.length); // 2

<h3>Expecting "one", "oneNested", 3 and 2...</h3>
<div id="one">
  <div id="oneNested"></div>
  <div></div>
</div>

Solution 5 - Javascript

This would work, you need to invoke the alias using call() or apply() with the appropriate context.

func.call(context, arg1, arg2, ...) 
func.apply(context, [args]) 

var x = document.querySelectorAll;
x.call(document, 'body');
x.apply(document, ['body']);

Solution 6 - Javascript

I took @David Muller's approach and one-lined it using a lambda

let $ = (selector) => document.querySelector(selector);
let $all = (selector) => document.querySelectorAll(selector);

Example:

$('body');
// <body>...</body>

Solution 7 - Javascript

function x(expr)
{
    return document.querySelectorAll(expr);
}

Solution 8 - Javascript

Here is my take on it. If the selector has multiple matches, return like querySelectorAll. If ony one match is found return like querySelector.

function $(selector) {
  let all = document.querySelectorAll(selector);
  if(all.length == 1) return all[0];
  return all;
}

let one = $('#my-single-element');
let many = $('#multiple-elements li');

2019 update

Today I made a new take on the problem. In this version you can also use a base like this:

let base = document.querySelectorAll('ul');

$$('li'); // All li
$$('li', base); // All li within ul

Functions

function $(selector, base = null) {
  base = (base === null) ? document : base;
  return base.querySelector(selector);
}

function $$(selector, base = null) {
  base = (base === null) ? document : base;
  return base.querySelectorAll(selector);
}

Solution 9 - Javascript

If you don't care about supporting ancient, awful browsers that nobody should be using anymore, then you can just do this:

const $ = (sel, parent = document) => parent.querySelector(sel);
const $$ = (sel, parent = document) => Array.from(parent.querySelectorAll(sel));

Here's some examples of usage:

// find specific element by id
console.log($("#someid"));
// find every element by class, within other element
// NOTE: This is a contrived example to demonstrate the parent search feature.
// If you don't already have the parent in a JavaScript variable, you should
// query via $$("#someparent .someclass") for better performance instead.
console.log($$(".someclass", $("#someparent")));
// find every h1 element
console.log($$("h1"));
// find every h1 element, within other element
console.log($$("h1", $("#exampleparent")));
// alternative way of finding every h1 element within other element
console.log($$("#exampleparent h1"));
// example of finding an element and then checking if it contains another element
console.log($("#exampleparent").contains($("#otherelement")));
// example of finding a bunch of elements and then further filtering them by criteria
// that aren't supported by pure CSS, such as their text content
// NOTE: There WAS a ":contains(text)" selector in CSS3 but it was deprecated from the
// spec because it violated the separation between stylesheets and text content, and you
// can't rely on that CSS selector, which is why you should never use it and should
// instead re-implement it yourself like we do in this example.
// ALSO NOTE: This is just a demonstration of .filter(). I don't recommend using
// "textContent" as a filter. If you need to find specific elements, use their way
// more reliable id/class to find them instead of some arbitrary text content search.
console.log($$("#exampleparent h1").filter(el => el.textContent === "Hello World!"));

The functions I provided use a ton of modern JS features:

  • const to make sure the function variable can't be overwritten.
  • functions defined as an arrow function aka lambda: (args) => code with implied return statement
  • default parameters (not supported by browsers before the year 2016)
  • no {} or return, since those can be skipped if there's just 1 statement in the function body.
  • The modern function Array.from() is used, which converts the querySelectorAll result (which is always a NodeList, or empty NodeList), into an Array, which is basically what every developer wants. Because that's how you get access to .filter() and other Array functions that allow you to process the discovered nodes further, using clean, short code. And Array.from() creates a shallow copy of all elements which means that it's blazingly fast (it just copies the memory references/pointers to each Node DOM element from the original NodeList). It's a major API enhancer.

If you care about ancient browsers, you can still use my functions but use Babel to convert the modern JS to old ES5 when you release your website.

My suggestion would be to write your entire site in ES6 or higher and then use Babel if you care about visitors from Windows XP and other dead operating systems, or just random people who haven't updated their browsers in 5+ years.

But I wouldn't recommend using Babel. Stop worrying about people who have old browsers. It is their problem, not yours.

The modern "app" world is incredibly deeply based on web browsers and JavaScript and modern CSS, and most of your visitors these days have modern, auto-updated browsers. You basically can't live modern life without a modern browser, since so many websites demand it now.

In my opinion, the days of expecting web designers to waste their time and sanity trying to make a site work on browsers from 1993 are over. It's time the laziest customers/visitors update their old browsers instead. It's not difficult. Even old and dead operating systems usually have ways to install new versions of browsers on them. And people who don't have an updated browser are only a tiny fraction of a percent these days.

For example, the Bootstrap framework, the world's most popular framework for mobile/responsive sites, only cares about supporting the 2 most recent major versions of all major browsers (at least 0.5% market share). Here's their list as of this moment:

  • 0.5% market share or higher
  • last 2 major versions only
  • not a dead browser (not discontinued)
  • Chrome >= 60
  • Firefox >= 60
  • Firefox ESR
  • iOS >= 12
  • Safari >= 12
  • not Explorer <= 11 (meaning no versions of Internet Explorer at all)

And I completely agree with this. I was a web developer in the early 2000s and it was absolute hell. Being expected to make it work on some random, stupid user's ancient browser was hell. It took all the fun out of web development. It made me hate and quit web development. Because 90% of my time was wasted on browser compatibility. That's not how life should be. It's not your fault that some customers/visitors are lazy. And these days, visitors have no more excuses to stay lazy.

Instead, you should only target users who have modern browsers. Which is basically everybody these days. There is no excuse for anyone to use an old browser. And if they use an old browser, your site should show a big, fat banner saying "Please, join the modern world for your own sake. Download a new browser. How are you even able to live your normal life with such an old browser? Are you a time traveler from caveman times?".

People have no excuses to have old browsers anymore:

  1. Linux: Ships with the latest versions of Firefox by default.
  2. Mac: Ships with Safari, which is a modern browser. But some older macOS versions won't get newer versions of Safari, and older machines often can't install the latest macOS version either. Well, tough luck for those visitors. They are gonna have trouble on more than just your website. It's up to them to install a modern browser (such as Chrome), which they are able to do even on old versions of macOS. So they have no excuses. Don't waste your life catering to people on very old, buggy Safari versions.
  3. Windows: Windows 10 ships with Edge, which is based on Chromium and is as compatible with websites as Chrome is. People have no excuse to have an old browser. And most Windows users use Chrome. As for very old, discontinued versions of Windows (XP, Vista, 7, 8), well, we yet again arrive at the same question as before: Do you care about 0.0000001% stupid visitors who use a dead OS and an old Internet Explorer version? The whole freaking web will be broken for them anyway, so who cares if your site is broken for them too? They should stop being lazy and just upgrade their OS to Windows 10, or at least install Chrome or Firefox on their current OS. They have no excuses.
  4. iOS: If you're stuck on a super old iOS device, then you can't use the modern web. Tough luck. A lot of the web is gonna be broken for you. Get a new device. Even frameworks like Bootstrap, the world's #1 mobile web framework, doesn't support iOS 11 or earlier. It's not our problem. It's the cheapskate visitor's problem if they still hang onto such an old device. They can literally get a newer iOS device second-hand for almost no money at all and fix all of their problems with visiting the modern web. And they'll need to buy that anyway since most apps (even banking/important apps) require modern iOS versions.
  5. Android: The browser is independently updated from the OS, and can even be sideloaded, so even if you're stuck on old Android versions, you have access to modern browsers. So you have no excuses.

Most people these days have browsers that are completely up-to-date and auto-updated. That's the fact.

So yeah... the days of website designers suffering through hell just for catering to old browsers are over. Therefore I suggest that people use ES6 and CSS3 for their websites, to make web designing a joy for the first time.

Hope you enjoy the ES6 functions I provided!

Solution 10 - Javascript

> function $(selector, base = null) { > base = (base === null) ? document : base; > return base.querySelector(selector); > } >
> function $$(selector, base = null) { > base = (base === null) ? document : base; > return base.querySelectorAll(selector); > }

Why not simplier ??? :

let $ = (selector, base = document) => {
  let elements = base.querySelectorAll(selector);
  return (elements.length == 1) ? elements[0] : elements;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmikemaccanaView Question on Stackoverflow
Solution 1 - JavascriptSunday IronfootView Answer on Stackoverflow
Solution 2 - JavascriptaloisdgView Answer on Stackoverflow
Solution 3 - JavascriptzlebnikView Answer on Stackoverflow
Solution 4 - JavascriptAndrew WillemsView Answer on Stackoverflow
Solution 5 - JavascriptGabeView Answer on Stackoverflow
Solution 6 - JavascriptsvarogView Answer on Stackoverflow
Solution 7 - JavascriptDavid MüllerView Answer on Stackoverflow
Solution 8 - JavascriptJens TörnellView Answer on Stackoverflow
Solution 9 - JavascriptMitch McMabersView Answer on Stackoverflow
Solution 10 - JavascriptMarek LisieckiView Answer on Stackoverflow