

Remember last week when I spoke about how to use
querySelect()
and querySelectAll()
and how it could all be done in one command I called qs()()
? Remember reading the part about how after some research, I've had though about redesigning the function?Here's what the old version looks like.
var qs = parent => (query,all) => (typeof(parent) === "string") ? qs(qs()(parent))(query,all) : (parent||document)[`querySelector${(all||false)?"All":""}`](query);
And here's the new version of
qs()()
.var qs = parent => query => {let q; return (typeof(parent) === "string") ? qs(qs()(parent))(query) : (q = (parent||document).querySelectAll(query)).length > 1 ? q : (q[0]||null);};
Try as I might, I really hoped that that
let q
(or more appropriately var q
) would have slyly stuck itself into the q = (parent||document).querySelectorAll(query)
part where assigning q
there allows us to have q.length > 1
without having to put our assignment operation (=
) on a separate line with the let q
statement, but I cannot do that, and here's why.Remember that when you assign a value to a variable, the assignment operator returns the value assigned to that variable. You can try it out in the debugging console of your browser or in a Node.js console right now. If you type in
five = 5;
the console will return 5
. That 5
is stored in the variable five
until your refresh your browser or exit the Node.js console.I could go on about operator precedence in JavaScript, but I don't want to.
I could have gone without the
let q
statement and not used the curly braces ({...}
) and return
statement as JavaScript variable hoisting would have allowed me to assign a value to q
(with q = (parent||document).querySelectorAll(query)
) without initially declaring q
with the let q
statement but the scope of using the let
keyword inside a set of parenthesis would not allow me to get q.length
because if you use let
, var
, or const
before a variable, it will return undefined
, and undefined.length
throws a TypeError
. And if you are using strict mode, either by putting "use strict";
at the top of your file or at the beginning of your function, declaring q
without let
, var
, or const
throws a ReferenceError
.// Let's review (assuming strict mode is not initially set in this figure)
five = 5; // => 5 (because the assignment operator returns the value that is assigned to the variable; because strict mode is not enabled, five is hoisted, and can be initialized without declaration first)
var six; // => undefined (declaration keywords (var, let, and const) return undefined even though the variable. If six was previously used, it would have been hoisted.)
var seven = 7; // => undefined (although 7 is assigned to seven; seven is declared and assigned so technically it's hoisted)
var num = x => {
"use strict";
let w; // => undefined (although w is defined)
y = x; // => ReferenceError (because y was not defined)
w = (var z=[x,y,3]).length; // SyntaxError (because unexpected token "var" next to z) (also, if it had worked the code inside the parenthesis would have returned undefined, of which undefined.length is a TypeError)
return w;
};
At any rate, because our
let q
couldn't be integrated into our one-liner, we have to bring back the curly braces ({...}
) and return
statement, since we can't return our q
value implicitly.//Remember that this form, known as FUNCTIONAL FORM,...
var f = x => x;
// Means the same as this form, known as FUNCTIONAL-IMPERATIVE FORM,...
var f = x => { return x; }
// Means the same as this form known as IMPERATIVE FORM
var f = function(x){ return x; }
So here is the new
qs()()
in detail./* @func: qs
* @desc: querySelector[All] in one function
* @param parent : (@default is document) A string or Element to find the query element(s)
* @param query : (required) A CSS string matching an id, class, or attribute of elements inside the parent.
* @returns:
* null if nothing is found.
* Node if query matches one result.
* NodeList query matches more than one result.
*/
var qs = function(parent){
return function(query){
let q; // q is declared in the inner function
if(typeof(parent) === "string"){
let parent_query = qs()(parent);
return qs(parent_query)(query);
} else {
// If parent is undefined, use document.
var parent_element = parent || document;
q = parent_element.querySelectorAll(query);
if(q.length > 1){
// if q match more than one instance return all matches as a NodeList.
return q;
} else {
// Otherwise, return the first element only
// Note: If [] is returned, and [][0] is undefined (meaning no matches), return null.
return (q[0]||null);
}
}
} // inner function
} // outer function
Before I talk about what makes this different from the old version, I want to talk about two functions in the
class. The Array
function matches the first instance of a query. The .find()
matches all instances of a query and returns an array. What if you use .filter()
.filter()
and it only returns one instance? You still have to tack on [0]
to get that one item out of the returned array. The .find()
function, is basically the .filter()
function where that one item is returned without needed to append [0]
if it finds something.There is one other thing to note. If
.filter()
finds no results, it returns an empty array, []
, but .find()
returns undefined
. Furthermore, the zeroth item in an empty array, [][0]
, is undefined
.The
.querySelector()
function is basically the .find()
function for a NodeList
, which makes .querySelectorAll()
the .filter()
function. But there is one problem: .querySelector()
doesn't return undefined
, it returns null
, and null
is not the same as undefined
.Just about all
.getElementsBy*()
function returns an empty array, like .querySelectorAll()
. But .getElementById()
returns null
like .querySelector()
. We still need null
any way because typeof([]) === "object"
and typeof(null) === "object"
too. So if q[0]
is undefined
, null
will be returned.With that, we eliminate the
variable that was in the old version of all
qs()()
. While this function doesn't take advantage of template strings like the old version did, it finally eliminates .querySelector()
, meaning .querySelectorAll()
is the ultimate query function and the only one we need.So that's the new
qs()()
. Eventually, I'd like to present a new library I'm working on called haqs which is a JavaScript library which has an emphasis on closures and functional programming.Here at Hackernoon, I plan on describing the more simplified versions of some of these function. The haqs library version is a bit more complicated because I was a bit braggadocio with demonstrating the functional programming aspects. I would really like to see just an itty-bit of object-oriented programming to reduce the amount of repetition and static structuring.
I also want to apply functional patterns to write code with a concise behavior. Haqs isn't quite ready yet, but I integrate some of the stuff I'm talking about here into it as I would like for it to be used in my Codepen work.
We'll see how it turns out. Until next time, keep hacking!
Create your free account to unlock your custom reading experience.