DEV Community

Cover image for Why I won't use querySelector again.
Ayobami Ogundiran
Ayobami Ogundiran

Posted on • Edited on

Why I won't use querySelector again.

The JavaScript ecosystem is always evolving and it is time for another wave of the future.

We all loved it when jQuery was baked into browsers as document.querySelector() and document.querySelectorAll(). The DOM methods make accessing the DOM a lot better.

Recently, I had some experience that practically proved to me that using both document.querySelector() and document.querySelectorAll() is sticking to the distant past while missing many juicy features in Javascript.

Using document.querySelector() and document.querySelectorAll() means you can't destructure the DOM, access the DOM consistently, simplify multiple selections and reduce typing difficulties. Now, let's dive deeper so that you can realize all you're missing already.

But wait! If you don't really understand what querySelector() and querySelectorAll() are, and you want to learn more about them; check out the video tutorial below: JavaScript DOM manipulation

Inconsistent methods

document.querySelector() and document.querySelectorAll() are not consistent in selecting the DOM. One selects the first instance of the target selector while the other selects all instances of the target selector. That means one is used to select an individual element while the other is used to select a group of elements.

So you can't use both interchangeably. You have to keep on switching from one to another depending on what you want to select.

// Select the first element of the class .post
const post = document.querySelector('.post');

// Select all elements of the class .post
const posts = document.querySelectorAll('.post');
Enter fullscreen mode Exit fullscreen mode

Now, let me show you a better way to do it. I just built a UI library called koras.jsx that comes with $select()
which can be used in place of both document.querySelector() and document.querySelectorAll() in JavaScript and below is how to use it.

//select all instances of class .post
const posts = $select('.post');

//select the first instance of class post
const firstPost = $select('.post[0]');

//Doing the same with querySelector

const posts = document.querySelectorAll('.post');
const firstPost = posts[0];
Enter fullscreen mode Exit fullscreen mode

How cool is that? And it just works for the use cases of both document.querySelector() and document.querySelectorAll().

Selection by index

You can select an element among grouped elements at any position.

//select 6th instance of class post
$select('.post[5]')

//select 7th instance of div elements
$select('div[6]')
Enter fullscreen mode Exit fullscreen mode

Deleting elements

You can delete elements with $select() like:

//Delete only the instance of .post with index is equal to 2
$select('.post[delete|i=2]');

//delete all instances of .post with index greater than 2 
$select('.post[delete|i>2]');
Enter fullscreen mode Exit fullscreen mode

Adding attributes

You can add attributes to elements with $select()like:

$select('.post[add|class=hidden]');

$select('.post[add|style=1px solid red]');

$select('.post[add|id=2]');

$select('.post[add|class=flex bold]')
Enter fullscreen mode Exit fullscreen mode

You can remove attributes from elements with $select()like:

$select('.post[remove|class=hidden]');

$select('.post[remove|id]');

$select('.post[remove|class=flex bold]')

$select('.post[remove|style]');

//multiple constraints

const targetPost = $select(".post[filterIn|id=3, toggle|class=hidden]");
Enter fullscreen mode Exit fullscreen mode

Sorting DOM elements

const shuffledPosts = $select(".post[sort|order=shuffle]");
Enter fullscreen mode Exit fullscreen mode

$select and search with constraints

//usage as an event handler.
$select("tr[search|textContent=*" + this.value + "]");

//usage in a component
const matchedRows = $select(`.row[search|textContent=*${searchTerm}]`);
Enter fullscreen mode Exit fullscreen mode

DOM destructuring

DOM destructuring is one of the features shipped in es6 but you would be surprised you can't destructure the elements selected with document.querySelector() and document.querySelectorAll().

Not working:
 const [posts, comments] = document.querySelectorAll('.post, .comment');
Enter fullscreen mode Exit fullscreen mode
Solution:
const [posts, comments] = $select('.post, .comment');
Enter fullscreen mode Exit fullscreen mode

Can you see that? document.querySelectAll() doesn't support DOM destructuring. That means you're missing out on destructuring the DOM. No! That is not cool at all so you have to start using $select().

Repetitive methods

Using document.querySelect() or document.querySelectorAll() forces you to repeat themselves unnecessarily because they don't support DOM destructuring and that makes it a bit messy to maintain JavaScript code.

Won't work
const [audio, posts, comments] = document.querySelectorAll(".audio, .post, .comment");
Enter fullscreen mode Exit fullscreen mode
So you have to do:
const audio = document.querySelector("#audio");

const posts = document.querySelectorAll(".post");

const comments = document.querySelectorAll(".comment");
Enter fullscreen mode Exit fullscreen mode

Can you see how repetitive that is and how it will make it a bit messy to maintain your codebase at scale?

See, you can do the same using $select() like below with ease:

Solution
const [audio, posts, comments] = $select("#audio, .post, .comment");
Enter fullscreen mode Exit fullscreen mode

Difficulties in debugging DOM selections:

Using document.querySelector() tends to create more surface area for bugs to hide compared to $select() because of its support for DOM destructuring and multiple selections.

const audio = document.querySelector("#audio");

const posts = document.querySelectorAll(".post");

const comments = document.querySelectorAll(".comment");

// OR

const [audio, posts, comments] = $select("#audio, .post, .comment");
Enter fullscreen mode Exit fullscreen mode

$select() reduces the number of lines of code to write to select elements and so reduces the surface area for bugs to hide. That means bugs are more likely to be discovered comparatively faster while using $select().

Ability to use all array methods.

Elements selected with querySelector can only work with forEach but not map(), filter() and co.

$select()works with all array methods by default unlike document.querySelectorAll() which requires some workarounds that may lead to unnecessary repetition and complexity at scale.

Maintainability

Maintainability is always revolving around ease of use, brevity and consistency. Any codebase that is concise, consistent or easy to use tends to be maintainable. It is messy to maintain repetitive, and verbose code because you have more surface area to look at especially while fixing a bug or building a feature.

Shipping Less JavaScript

It is a rule of thumb to ship less JavaScript where possible and $select() makes it feasible while dealing with DOM selection. $select() is preferable to document.querySelector() and document.querySelectorAll() at scale because it is more concise and less repetitive. The more elements you select, the less you repeat selection with $select().

Appending or prepending array of elements.

You can't append an array of elements generated by $select() to another element like element.append(arrayGeneratedBy$Select) because the DOM expects NodeList.

Still, you can tell $select() to disable all of its superpowers by passing false as a second parameter to it.

//superpowers are on by default but you can pass false to off it.

 $select('.post', offSuperpowers);

 $select('.post', false);
Enter fullscreen mode Exit fullscreen mode

Now, it will return a normal DOM NodeList[] that is appendable and prependable.

$select() can do that

You maybe curious if $select() can do everything document.querySelect() or document.querySelectorAll() can do. Yes! It can do whatever they do. $select() can do it all. It can use tagor css selectors and do every other things query.querySelector()can do.

Is document.querySelector() any better?

It is possible to claim that querySelector() or querySelectorAll() is faster than $select()with about one millisecond on average but $select() wins it back with interests at scale.

You still need to learn to use querySelector because understanding how to use it gives you a chance to build something useful on it just like $select(). I am able to build $select() because I truly understand how it works.

I have made a tutorial on it. Check it out below: JavaScript DOM manipulation

Why I won't use querySelector ever again

By supporting DOM destructuring and enabling array-based selections, $select() proves to be a valuable addition to my toolkit. It not only improves the readability of my code but also minimizes the surface area for bugs, making debugging a more straightforward process.

In the search of maintainability, brevity, and consistency, $select() is a good choice for DOM selection, showing a step forward in the evolution of JavaScript development.

As we continue to adapt to the evolving JavaScript ecosystem, using tools like $select() ensures that we keep making our code more concise, readable, and maintainable.

Top comments (155)

Collapse
 
moopet profile image
Ben Sinclair • Edited

I'm looking at your initial issues and I don't think they're issues I've ever considered.

document.querySelector() and document.querySelectorAll() are not consistent in selecting the DOM. [...] one is used to select an individual element while the other is used to select a group of elements.

So I'm not sure why this is a problem. Yes, they do different things, but that's normal - you write different functions to do different things. I think of it as querySelector being a bit of sugar for querySelectorAll(...)[0]. If they did the same thing, then there would be no point in one of them existing!


Destructuring the return value from querySelectorAll is definitely something I've never thought of, probably because I don't think it makes sense outside a very narrow use case.

Every time you select something with multiple rules, there's a Venn diagram of DOM elements and their selectors. Unless you're identifying everything with a unique class.

Maybe your e-commerce forms all need some CSRF logic, so you ask something like, querySelectorAll('form.stripe, form.paypal, form.foobarFintech'). You don't want to have to destructure that and iterate through the results, you want a list of relevant forms to bind to.

I'll admit, querySelectorAll falls down a bit here because it doesn't return an array, but it's so common to spread it into one that it doesn't really cause much friction.


Adding a new DSL for things like $select('.post[remove|class=flex bold]') is another odd choice, to me:

First, it's baking an action into the parameter of a function named like a getter. I would rather take the return value from the getter and perform an action on it, or name the function something else.

Secondly, I can't guess from looking at it whether it removes "flex" OR "bold", "flex" AND "bold", or "flex space bold" (i.e. whether order counts).

Finally, $select('.post[5]') is reinventing document.querySelectorAll('.post')[5] except I can't intuit what exception handling there is.

Collapse
 
skhmt profile image
Mike 🐈‍⬛

the idea that you ship less js because $select is shorter than document.querySelectorAll yet you need to pull in an entire library to use $select is a strange one too

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited

$select alone is written in few lines of code and you don't write less code because $select() is shorter — it is far from that but you have to use it to know what I am saying.

In most projects , you will end up writing more lines of code if you don't use $select(). That is why it scales well and still comes with several goodies to enjoy. 🤩

Collapse
 
dansasser profile image
Daniel T Sasser II

This right here folks!

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited

I'm looking at your initial issues and I don't think they're issues I've ever considered.

First, they do not do the same thing — check the docs I attached for confirmation.

Second, I will say you're right not to deny your experience but if you really want to have a better experience just try it. Then, you will have balanced insight concerning both APIs. So check it out and use it, then let's have this discussion.

Also, you can get returned elements and still do whatever you want with them because $select() always returns all the elements it selects.

It can filter, search the DOM directly and more. And you can use it comfortably, directly, concisely and flexibly in html via onclick, onhover and more.

Parent children input component is a good example of how to use it and you can check its code on codesandbox.

Thank you!

Collapse
 
moopet profile image
Ben Sinclair

Could you help me out with the source for that preview? It looks like it's a page embedding a ton of minimised scripts.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

I am the source because I made it.

Live version

Full image:
Here is the full image

Note: The library comes with another tool to make JSX work directly in browsers and servers without a virtual DOM or tagged templates. So the image contains a component that uses $select() and $render()

Or what source are you referring to? Thank you.

Thread Thread
 
moopet profile image
Ben Sinclair

Sorry, I tried it in a different browser and it worked. The "sandbox" link in the corner was missing in my Firefox (probably an over-zealous adblocker or something). I can see it now.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran

Thanks for taking your time. I understand ✅

Collapse
 
j-256 profile image
James

1000%. Everything you said is right. This is a weird solution to a nonexistent problem. Dev.to strikes again!

Collapse
 
codingnninja profile image
Ayobami Ogundiran

Have you checked it yet? Check it and then review your opinion. The person you're replying seems to have reviewed his opinion.

Thread Thread
 
j-256 profile image
James

Can you tell me what I'm missing? This is all much ado about being able to destructure, right? Why is that so important? It seems like mostly a preference thing to me, i.e. do you want to have very wide lines or progress stepwise.
querySelector is solved by just not using it, so all that leaves is destructuring and I'm always very dubious of wheels being reinvented.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran

This is not re-inventing the wheel.

See, $select is an accessory for composition. If you have a component, it is not convenient to keep re-writing every operations.

For example if you want to search a table, you need to write an operation to do that in different cases but you just need a line of code when you use $select.

Check it in action here

Also, $select and $render that come with koras.jsx make it possible to use composition like in React with only vanilla JavaScript which is impossible using querySelectorAll because it is not really composable.

Collapse
 
zorqie profile image
I, Pavlov

So, let's abandon all SOLID principles and move everything from javascript syntax to something else?

//Delete only the instance of .post with index is equal to 2
$select('.post[delete|i=2]');
Enter fullscreen mode Exit fullscreen mode

Is deleting something in a function called select really a great idea? Are we deleting from the selection or from DOM?

It's convenient. But is it useful?

Collapse
 
codingnninja profile image
Ayobami Ogundiran

It is both convenient and useful. It does both.

You can filter, search, order and more. Check the docs to have a better understanding of it.

Collapse
 
zorqie profile image
I, Pavlov • Edited

You're moving away from both JavaScript and CSS by trying to cram too much functionality into a "better querrySelector" while breaking its single responsibility: return nodes matching a CSS selector. For example:

const [posts, comments] = $select('.post, .comment');
Enter fullscreen mode Exit fullscreen mode

The CSS selector '.post, .comment' means "all nodes with either class=post OR class=comment", but instead $select returns all nodes with class=post AND all nodes with class=comment. I realize the result contains the same nodes but I request one list and I get two. Not cool. And if there's a <div class='post comment'> and I get it in both lists, that may or may not be what I expect.

I understand, CSS selector syntax may not be the most intuitive but it's well established and documented. And it would be expected that the same selector returns the same node list everywhere. E.g. '*[something]' is a valid attribute selector but '*[5]' is not and it won't work anywhere else. So the selector isn't CSS and the indexing/filtering isn't JavaScript. Which is why I think it may be convenient but not very useful.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

Your explanation is faulty.

'.post, .comment' means you want to select all nodes with post class and comment class which is exactly what querySelectorAll does but with some errors but $select() does it correctly.

If you want to select an element with two classes like 'div class='post comment' , you will use '.post.comment'; that is, a tag with both post and comment classes. That is how to use CSS selector.

If your CSS selector query is correct, then it will work in $select(). Maybe you should check CSS documentations for reference.

It seems you're misusing CSS selectors because $select works exactly like querySelectorAll but without its error.

*[something] is meant to work in JavaScript but not in css and standard CSS selectors work as expected. So your concern is not necessary.

Filtering, search and co use JavaScript behind the scenes so they're JavaScript. Everything works well as you would expect them in css or JavaScript.

$select() is designed to work in JavaScript and also works with all standard CSS selectors.

Thanks for the comments.

Thread Thread
 
moopet profile image
Ben Sinclair

I think there's a confusion of "and" and "or" terms here.

'.post, .comment' means select all nodes with the "post" class, plus all nodes with the "comment" class". We could phrase this either way in English which makes it a bit tricky to have casual comment threads about it without tripping up.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

Are you telling me that is not what you do in querySelectorAll for multiple selectors? querySelectorAll only has some errors but $select() fixes them.

Why do you want to criticize $select() for making what querySelectorAll fail at work well?

Thread Thread
 
zorqie profile image
I, Pavlov

The CSS selector list (,) selects all the matching nodes. A selector list is a comma-separated list of selectors.

[(developer.mozilla.org/en-US/docs/W...]

So yes, you're correct, it's AND but it's one resulting list. When I specifically want to process all matching nodes at once, I use selector list. That's the meaning of comma in CSS. In $select comma has a different meaning.

I think it will be cleaner/more consistent if $select returned multiple lists when passed multiple selectors and one list when passed a single selector list, like this:

const allTogether = $select('.post, .comment, div, #main, [attribute=value]');
const [posts, comments, other] = $select('.post', '.comment', 'div, #main, [attribute=value]');
Enter fullscreen mode Exit fullscreen mode

I understand you use JavaScript behind the scenes, but document.querySelectorAll('.post')[6] is standard array indexing syntax, something all JavaScript developers are familiar with. $select('.post[6]') is neither standard JavaScript, nor standard CSS. It's valid JavaScript but if I need to use a variable instead of literal, it gets ugly. I understand there may be performance gains from returning faster after finding the n-th element in a large document. But CSS already has the powerful but awkward :nth-child() and JavaScript has .filter().

On a personal note, the quotes around "software engineer" on your github profile make it look like you're not confident you're a "real" software engineer. Which you are, and a good one. Cheers.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

If you have checked the documentation, you would have realized that is exactly how it works.

If you get just an element, it returns only the element just like you said.

Now, give me my flowers that have already done it before you say it.

This is why I tried to refer people to use the tool first so that we can have conversations that actually help.

What you raised, in this reply, shows you now have a balance view. And I know JavaScript has filter but it has nothing to do with the DOM. This one filters DOM elements directly after selections. Doing it with JavaScript is not as comfortable.

Also, I will look into what you said further to see if I can improve the tool further with you suggestions.

Thanks! Use the tool because it works exactly as you expect.

Collapse
 
bardsley profile image
bardsley • Edited

I’ve checked the docs and I think a function named select that deletes things is a terrible idea. Naming things (and caching) is hard but this is a really bad example of doing it wrong. A function should give some indication of what it does from its name. Yours does not. Sorry thumbs down from me

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran

Have you used it? Use it and see how your opinion changes. Thumbs up.

Collapse
 
moopet profile image
Ben Sinclair

I would be unsure what $select would return in a query such as that. Does it perform the action before or after constructing the array of elements to return? There are a lot of readability issues with combining actions like this.

Collapse
 
oculus42 profile image
Samuel Rouse

I appreciate the willingness to try something new, but the divergence from the CSS selector specification makes this difficult to recommend. It is a new but overlapping DSL with CSS, and it moves a lot of functionality into multi-purpose strings which breaks many core developer experience recommendations:

  • Single-responsibility – The selector string can get an element, an array, an array of arrays, can filter to a specific element, and can modify or remove elements. We can't look at the function being called to know what will happen; we have to reason about the selector string.
  • Reusability – Actions can be tied into the selectors, so we can't necessarily create reusable functions to take a selector and perform a series of actions on it, because the selector itself might do work.
  • Discoverability – Developers can't see the methods on a utility or return object, they have to internalize the rules of the library. Rather than a jQuery $('.code').attr('id', 2); you have $select(".code[add|id=2]"); This also reduces the ability for tools like IntelliSense and ESLint to provide coding support and improve developer efficiency and accuracy.
  • Consistency - $select appears to return an element for a single result like querySelector. This seems like a bug factory to me. The developer must guarantee the selector count to know the return type, or add logic to test for it, negating the benefit. querySelector and querySelectorAll serve different purposes, and this eliminates the developer cues that would tell you which output type to expect. It seems most examples navigate around this by using multiple selections to ensure an array response?

It would make more sense to me if $select accepted more arguments. Rather than the comma living inside the "selector" string and serving a different/overloaded purpose than the standard CSS comma, this would provide equivalent functionality and, in my opinion, be more consistent with both CSS and clarity of the destructurable result array. I have three argument selectors passed into the function, so I will get an array of three results back.

const [audio, posts, comments] = $select("#audio", ".post", ".comment");
Enter fullscreen mode Exit fullscreen mode

Thank you for posting your thoughts and ideas. It's interesting to hear other perspectives!

Collapse
 
moopet profile image
Ben Sinclair

Yes, according to the docs, $select returns a single item, an array of items, or null, depending on matches. Using querySelectorAll always returns a list, so you don't have to check for nulls, and if you convert it to a regular array you can iterate as normal even if the array is empty, which makes querySelectorAll the clear winner for me in this particular use case.

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited

What will querySelectorAll return if it matches nothing?

How would it work if you use multiple selectors with it? querySelectorAll has a bug and it won't work.

See, check the docs for querySelectorAll, you would realize it is meant to do multiple selections but it doesn't work because it is faulty but $select() fixes it.

Thanks!

Thread Thread
 
moopet profile image
Ben Sinclair

It'll always return a NodeList - an empty NodeList if it matched nothing. It has a predictable return type.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

Now, you're raising something that is worth checking. That is why I refer people to check the tool first.

I will check this out and see how I can improve the tool with your suggestions.

What are you suggesting would be a predictable return type in this case? And how is this one not predictable depending on your selections?

Remember, you're selecting multiple different thing. So the corresponding response should determine the type that is returned.

You won't select with an #id and expect to return an empty array when nothing is matched.

So, please suggest in your opinion what to return to have consistent type because $select returns consistent types already. Let me understand your suggestions by digging deeper.

Thanks for taking your time.

Thread Thread
 
moopet profile image
Ben Sinclair

If something is returning a set of results, I personally prefer that it always does so.

For example, if I search for .post and there's only one on the page, then with querySelectorAll I still get a NodeList back, so the same code which works for a multiple results works for a single one. That means I don't have to apply any conditions to check whether it's an object or an array.

A lot of systems work the way you've implemented it (I'm looking at APIs in particular) but it adds a step we don't really need.

I don't really like to use querySelector because I have to do a null check. querySelectorAll does the same thing and is consistent. I can then choose to take the first result or check whether the result is empty, and I have the extra info to check whether multiple results were returned when I only expected one.

So far as I read, $select returns consistent types (an object for a single match, an array for multiple selectors, an array for multiple matches of a single selector, an array of arrays for a mixture), but since this is dependent on the contents of the DOM at the time, I can't look at the code and know what the return type will be.

In particular, if I come to change the selector in the future as part of a theme update or something, I have to use more brain to figure out whether I need to update the code because the variable might change type.

I think if I'd tried to do what you're doing, I'd have made an explicit destructurableQuerySelectorAll function to address the issue you first mentioned without bundling in extra functionality.

I'm also rarely, if ever, going to use #ids in my HTML. I'm going to use classes even if something only appears once, and [data-foo=bar] attributes if their purpose is purely javascript. By nature, these are all things that are legal to repeat in the code, so as far as my code (or a linter) is concerned, the way I process the results will always be the same.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran

Thanks for the detailed reply. I will definitely use some of your suggestions to make it better.

Cheers!

Collapse
 
codingnninja profile image
Ayobami Ogundiran

Let me address your comment by saying $select() does everything you have mentioned better than querySelector and querySelectorAll. You just need to use it to find out. Check it out in the library.

Collapse
 
perry_rylance_ee9029eff04 profile image
Perry Rylance

So, jQuery but without the established standards or maturity. I don't want to discourage you but this isn't something I'd recommend people adopt. Keep using it and you'll find out why. Happy coding!

Collapse
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

Precisely. Once he gets inconsistent results based on the number of nodes returned, he will realize why this is a bad idea. And once he learns SRP, he will know why this breaks it, and badly.

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited

Please show me how I would get inconsistent results. That would be more helpful. I have tried on my own to figure possible issues and I will appreciate it if you point it out in this case.

$select is structured. Please point out how it is not and rest assured I will improve it based on your suggestions and even give your credit for it.

Anyway, I am not scared of learning because I have been learning for several years. Learning is a source of growth.

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

Okay, let us take this as an example...

Say I have a dynamic view with 0 - n divs.

What does your $select return when there are...

0 found
1 found
more than 1 found

Advise me of that and perhaps I can show you what I am trying to explain. Use 'div' as your selector.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

What does your $select return when there are...
0 found
1 found
more than 1 found. Advise me of that and perhaps I can show you what I am trying to explain. Use 'div' as your selector.

  1. It returns "null" when 0 element is found.

  2. It returns the only found element

  3. It returns an array of all the elements found.

That is it. I am expecting your response.

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

That is a serious bug, as you now need to run multiple checks against EVERY call to $select to check what it is returning. That scenario REQUIRES querySelectorAll so that you get an expected response type as you don't know from the outset how many you will get. All you need is a function that wraps querySelectoraall and returns an array, which you can then easily deconstruct. That would be two lines of code and 3 lines for core function. Your code would require about 8-9 lines of code for every call to $select. I am sorry, but that is just a beginner mistake.

And that doesn't include your breakage of the single responsibility principle by allowing deletion in a function that is meant for selection. And changing the selector syntax too. If CSS changed to include that syntax, your code would cease to work.

If you want to see code samples that would vastly improve your code, then I will happily supply them.

Also, what bug is in querySelectorAll or querySelector? I have never found a bug in either. Please elaborate on this "bug".

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

I appreciate your comment and I have already finalized the case of returning null with someone else in the comment section.

I see many people suggesting using [...document.querySectorAll(".post, .comment")] which will return unexpected result.

I wish I was wrong but it seems MDN docs for it is not consistent with expectations that is why we don't destructure multiple arrays of selected elements.

You will understand my point better when you use $select().

It would be good to try out what you suggest here because you can't destructure querySelectorAll for multiple selections.

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb • Edited

I have no idea why you think you can't destructure it. Just write it so that it works for your use case.

You can write a function that converts a NodeList to an array and then you can destructure it with ease.

function $(selector, root = document) {
    return root.querySelector(selector);
}

function $$(selector, root = document) {
    return Array.from(root.querySelectorAll(selector));
}

const [divTheFirst, divTheSecond, divTheThird] = $$('div');
Enter fullscreen mode Exit fullscreen mode

See how easy that was? A consistent return type of Array for the $$ function, so no unexpected behavior due to the change in return type and easily destructured. The above is 6 lines of code for two core functions that you can use where required and no need to check for null, check via Array.isArray, etc. It just works in an expected, consistent fashion.

Using the above, I'll never use querySelector, querySelectorAll OR $select ever again :D

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

It is seems you don't get my point. Can you select multiple group of elements with your custom function without any issues? Like [divs, posts] = $$('div, .post');

You should know building what I built I should have tested things like this before arguing with you.

Now, go and test your theory, you might understand why the library is a better option.

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

If it's a valid CSS selector, then the code I provided will work every time.

If you want TWO discrete sets of elements based on two CSS selectors, then you would use two commands to do that, not one, and it would be folly/undesirable to do that in a single statement without breaking how CSS works.

So, again your library is either breaking CSS selector syntax, or you're simply trying to return two result sets from a single CSS selector. That's not how CSS selectors work, nor how they should work.

Your lack of understanding of the Single Responsibility Principle is not my problem. I'm showing you how to select single elements via a $ function. I'm showing you how to select arrays of elements using $$, which can be easily destructured and it's literally a few lines of code to do so, so the whole "I'll never use querySelectorAll again" statement is pure nonsense.

So long as you use valid CSS selectors, then you'll be fine, but if you don't know how CSS selectors work or how the Single Responsibility Principle operates, then I can't help you any further. Until you understand CSS selectors and SRP, then you won't be able to progress any further and your library will continue to cause you issues in the future. No real software engineer who tries it will continue to use it, given the bugs in it (lack of clear, expected interface for $select and its response, breakage of CSS selector syntax and lack of SRP).

Trust me, I am TRYING to help you understand where you're going wrong, but you don't seem to be able to grasp it, so look back over what I've provided and look up SRP first before responding again.

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

If you want divs and posts like you've suggested via this broken code:

[divs, posts] = $$('div, .post');
Enter fullscreen mode Exit fullscreen mode

Then, you should do this instead (using the code I provided):

const divs = $$('div');
const posts = $$('.post');

OR, if you like brevity over clarity..

const [divs, posts] = [$$('div'), $$('.post')];
Enter fullscreen mode Exit fullscreen mode

if you want all of the posts for a given div, then provide that as the root, then get the posts...

const root = $('#divTest');
const posts = $$('.post', root);

Or, again for brevity...

const posts = $$('#divTest .post');
or
const posts = $$('#divTest > .post');

depending on what you need.
Enter fullscreen mode Exit fullscreen mode

You're trying to solve a problem that doesn't really exist and doing it wrong by mixing and matching too many things together. Just because you can write a whole program in a single line of code doesn't mean you should do so. You should always write code for clarity of purpose, first and foremost. Everything else is secondary.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran

Thank you. I will definitely pick one or two things from your suggestions.

Please add filter, search, ordering and everything $select does to your code and you will get $select.

All the filtering, searching and co added to $select make it more reusable and composable and that is its main purpose.

I don't know why you think I am not heeding your advice. I do. You yourself would tell me to only improve it with your ideas by the time you use $select and $render.

Thank you for taking your time.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

You are assuming I don't understand SRP and everything you've said.

Please, I do.

I have worked with it from another angle you're not willing to consider or understand.

$select() in this case is a processor and it only does a thing by processing queries and I make sure people can't do anything else with it except its returned values because SRP.

You're making wrong assumptions
confidently without using a tool. You're trying to help but have you used the tool to have a proper context and come with a case where it has one of the issues your raised? No.

Or you just want me to take your opinion blindly when I have already done rigorous research on what you assume you know better than I do?

Please, I respect you for taking your time but try not to make wrong assumptions that have nothing to do with the library.

I used $select and $render, in combination, to solve a problem generally considered impossible by every JavaScript/frontend engineer. If I have followed every so called best practices blindly like you want me to, I wouldn't have been able to solve the problem.

I am a forever-learner. I have learned from you and others who raised genuine cases but don't expect me to just take your opinion blindly. Never! I have already started my own research based on opinions on this article.

Thank you.

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

I solved your problem already, without breaking SRP or standard CSS syntax, achieving it with less code and resulting in less code use than your $select does. And it if you wish to destructure the array, you can. If you wish to filter, map, reduce, whatever you need, you can, as it provides every base method that an array provides. There's literally nothing left for you $select to solve. $select breaks CSS syntax, breaks SRP and does not have a consistent return type as it returns null for 0 records, a single object for 1 and an array for multiple results, so it's completely unusable when you don't know how many results to expect.

The only thing you can't do with my code, and you shouldn't anyway, is return multiple result sets from a single invalid CSS selector e.g. 'div, .post'. However, like I already described, you should be doing that with two lines of code, not one, to get two result sets. That's far better coding style, more maintainable and doesn't require all the breakage that you introduced via $select.

I understand you're relatively new to this and you're learning, but you really need to listen to what people are saying to you. They're not all wrong and you're right. It's the other way round, unfortunately. Learn from it and move on.

Anyway, happy coding. Hope you can learn from your errors here and do well in the future!

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

Don't be condescending please. I am professional with more than 12 years of experience in this field and a lot of achievements under my belts.

Select doesn't break CSS syntax because I am great at system design and nothing of what you said. Please show me an example of a CSS syntax it can't use? You brought nothing but you're still arguing confidently without evidence.

I am not in this for opinion. I am a builder so bring any CSS syntax it violates. I am waiting!

I understand system design so $select is designed to not use its own operations once a query doesn't match its query. So any change in css queries is catered for.

In case CSS adopts div[remove|id], $select will still work because that is a $select query though we can ship another version of the library to use css alternative. So, that is not a problem because no error would be caused.

Please, don't assume I also didn't worry about everything you're worried about why building the tool. I am also a developer who has similar concerns like you do so I always put in efforts in this regard.

See, the part you focus on is not even the important part of $select. You will only understand what I mean if you have to repeat filter function many times.

How will the function you wrote shuffle DOM elements?

$select(.post[add|order=shuffle]);

You said that you have solved my problem, how will you do the operation above with your functions?

Good luck.

You're also emphasizing null check case as something to worry about. No, it is nothing to worry about because it is already fixed. It is a case of returning an empty array instead of Null to avoid null check but you make it look like it is rocket science. It is not that serious please.

And stop assuming what you know nothing about. Use it and bring the errors instead of assuming. Out of curiosity, how did you know something you have not used is unusable under a condition?

If you can't bring the errors here, you opinion doesn't count. If you bring the errors, I will fix them instantly.

It seems you want to prove your superiority but I am not in for that kind of contest.

If you really want to help, use $select first and you will clear your innocence and come up with better concerns and then your opinion will be more important.

Thank you!

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

I asked you how how $select works. You stated it returns null when no results, a single object when 1 result and an array when more than 1 result. That makes it unusable.

.My code returns an array from $$ regardless of selector, so far better than your lib already.

And given that $$ returns an array, you can map, filter, reduce, reverse or even shuffle, if that's what you need.

Your lack of expected return type is what kills $select dead in its tracks. If you have since fixed that bug then I will happily take another look. But if it still returns , null, singleton or array, then I won't argue any further as that is a HUGE bug in your design that needs to be rectified. Only a beginner would make such an obvious error of judgement. If you're not a beginner, then it is bamboozling that you would do that. But, like I said, if you have fixed that design issue, then I will happily try it out.

And BTW, you have been condescending in this thread to multiple people, ignoring their advice, so I am amazed that you are surprised by people responding in kind.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

You asked a question and I responded without trying to play smart because I really wanted to get your message. If not, I would have shifted the goal post.

If you noticed my response mannerism, I addressed opinions like "Your point is wrong" but hardly said "You're wrong" except when I said "You don't understand SoC yet" which was intentional to drive home a point.

For your information, I told the first person that raised the issue of empty array instead of Null that I have fixed it for the next version. If you have checked my comments, you would have seen I admitted they were right that I had updated the code for another version release.

I have definitely picked things to improve the library. And be informed, I am not offended by all you said, sometimes we misread intentions from words.

Anyway, I am still expecting your CSS syntax violation examples.

Cheers!

Thread Thread
 
peter_lamb_fa3fc2089b698c profile image
Peter Lamb

Let's go back to your own example...

div, .post
Enter fullscreen mode Exit fullscreen mode

You keep saying that there's a bug in querySelectorAll, which you haven't qualified thus far. What do you expect that selector to return that querySelectorAll is NOT returning correctly, in your view? If there's a bug in querySelectorAll, then I'm sure (in addition to myself) Google, Mozilla, Apple and other JS engine implementation vendors would love to hear about it. If you are expecting it to give you two result sets (you did state that previously), then the problem is not querySelectorAll, but your own expectations, as that should be done in two lines of code, not one. That's just not what CSS selectors are for, nor what they should be used for.

So, if you can explain the bug in querySelectorAll from your view, then perhaps we can figure out why you're so averse to using querySelectorAll or a simple wrapper like the one I provided ($$), which, as far as I can see, gives you every feature you're looking for but in far less code. But, if you're looking for two diverse data sets, from a single selector, then I can't help you as that's not how coding works.

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited

Thank you.

The first error in your opinion is that it can do whatever jQuery does that is embedded in browsers and more.

You need to use it so that you have a better understanding of what $select() is.

If you have not used it, your opinion won't be accurate . So you had better check it out to have a balance idea of how it is different from querySelectorAll.

Collapse
 
joshuaamaju profile image
Joshua Amaju

This seems like a skill issue, a lack of understand of Javascript and best practices.

If you want people to use your stuff, maybe you should actually engage with their objections and questions instead of just telling them that they're wrong and they should go use your stuff.

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited

Skill issue, what? Scratch that! People can object based on biases without using a tool and that is okay.

Our argument will be more reasonable if they have a balance view of the tool first. Arguing without understanding how a thing works is unnecessary.

For example, they said reusability but the tool is reusable. It returns elements they can manipulate further but how would they know without using the tool? That is why I said they should check it out and come back to have a discussion after that.

I noticed a person that checked it now has to raise opinions that are not correct judging from general standard after realizing the tool actually works.

Even when they have genuine concern, it is inaccurate without using the tool.

Another thing is they said single responsibility — are they referring to $select or the query passed to it?

Each part of the query does a thing which you can easily reason with — $select() is just a processor and does a thing by processing queries. Just like you won't expect a JavaScript engine to not process several functions, you won't expect a query engine not to process several queries.

But each part of every query does a thing too.

And for your information, there is no best practice, we only have practices that worked for some people in their codebases and they tried to generalize them.

Let me say this again, go check it out and you will understand why I direct people to check it out before arguing with them.

Thanks

Collapse
 
joshuaamaju profile image
Joshua Amaju

Even if they're wrong, you're asking people to invest their time on your stuff and you can't even do the bare minimum of engaging with them in good faith.

And for your information, there is no best practice, we only have practices that worked for some people in their codebases and they tried to generalize them.

That's is the definition of best practice.

It's seems you're not open to new information. I doubt you'll achieve your aim with your attitude here. Maybe some engagement on this article, if that's your goal.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

First, I am referring people to check it out out of good faith not to waste their time arguing on things they don't know yet

It is like me arguing with people living in New York city after showing me a picture of it. How realistic will my opinion be without visiting New York city?

Yes, they raised their cases but the cases are exactly what the tool does and I told them the tool does what they raised and direct them to check it for confirmation.

And people are now raising good concerns.

Let's leave best practices aside because that is another unending argument.

I hope you understand my view now?

And people are actually checking it and now raising genuine cases which they couldn't have raised without checking the tool.

I get your points anyway. Thanks!

Collapse
 
xyzt70 profile image
Mohamad Yahia

So just jQuery but with a weird syntax to delete or modify items.
Cool.

Collapse
 
codingnninja profile image
Ayobami Ogundiran

It is more than that. You will love the syntax. Just try it out.

Collapse
 
madeinmilwaukee profile image
Chuck Watson

hard pass

Collapse
 
frzi profile image
Freek • Edited

In larger projects your $select will become far less appealing and would even introduce more inconsistencies in the code.

  1. There's a reason why there's querySelector and querySelectorAll: It's because both functions return a different type. querySelector always returns Node | null whereas querySelectorAll always returns NodeList. Regardless of the of the input. This is a good thing because you, as a programmer, do not need to check whether your query returns null, a Node or a NodeList. Having two separate functions eliminates the guesswork.

  2. querySelector and querySelectorAll don't just live in document. Rather, they're methods of Element. Meaning you can call these functions on other DOM elements as well, allowing you to query only in specific sub-sections of the DOM. This is excellent for performance reasons, as the browser won't have to go through the entire document for every query. It also means I can pass my queried Elements to other functions who can then perform subsequent queries.

    // Get the second form in the document.
    const form2 = document.querySelectorAll('form')[1]
    validate(form2)
    
    function validate(formEl) {
        // Query all the inputs in this specific form element.
        const inputs = formEl.querySelectorAll('input')
        // etc...
    }
    

    I believe $select does not support something similar?

  3. The queries we write in querySelector and querySelectorAll follow the selectors syntax from the CSS spec. The fun thing about CSS? Counting starts at 1. Whereas your $select function allows for JS array syntax ([0]) which starts counting at 0. You now cause inconsistencies by combining two different cultures into one:

    const hmm = $select('div:nth-child(1)[0]')
    

If people are so bothered by writing the long queryBlahblah method names, never want a NodeList and are hypnotised by the idea of destructuring, then I've got 11 lines of code to ease their mind:

Element.prototype.$ = function (selector) {
  return this.querySelector(selector)
}

Element.prototype.$$ = function (...selectors) {
  const results = selectors.map((s) => Array.from(this.querySelectorAll(s)))
  return selectors.length == 1 ? results[0] : results
}

export const $ = Element.prototype.$.bind(document)
export const $$ = Element.prototype.$$.bind(document)
Enter fullscreen mode Exit fullscreen mode
const form2 = $$('form')[1]
const [inputs, selects, textareas] = form2.$$('input', 'select', 'textarea')
Enter fullscreen mode Exit fullscreen mode

(I do not condone this code, btw)

Sorry for the dogpiling :)

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited
  1. The issue of consistent type can easily be adjusted in $select if that is better. Why would you prefer return type of array instead of Null when nothing is found?

  2. You can also used querySelectorAll on elements selected with $select. It just that there is no reason to do so as you can select the nested elements together.

$select caches the DOM, so it doesn't hit the DOM every second.

  1. .post[1] is meant to simulate what querySelectorAll does but not css. If you want to select by using css, just use css and it works.

  2. $select is more useful for composition. You can easily use it in a component.

Thanks! Anyway, I will look into what you said to see how to use your suggestions to improve the library.

Collapse
 
sirjaf profile image
sirjaf

Well done my brother. Keep it up, don't let tech bros talk you down. These are same tech bros who said tailwind isn't going anywhere but today it's all history. Keep pushing for people to try it, when they try it they might start using. Carry go my brother. NB: Best of luck. If there is any constructive criticism, take it onboard and improve your tool. Forget about the nay sayer

Collapse
 
codingnninja profile image
Ayobami Ogundiran

Thank you for the support and inspiration.

I really appreciate it. Thanks!

Collapse
 
fernandels_boni profile image
Wéri Boni

I didn't even think about that before. But when I was looking for an element or a bunch of elements to select, I can always add a class. But I see the whole point here. I'll save lots of structuring works and there is less work to do.

Collapse
 
codingnninja profile image
Ayobami Ogundiran

Now, you get the point.

Collapse
 
lexlohr profile image
Alex Lohr

Oh, so you wrote another partial jQuery clone. Sorry to bust your bubble, but this is 2025 and we have modern frontend frameworks (react, angular, vue, svelte, solid, astro) to render DOM and keep its references so we only ever have to manually query dom nodes for our initial render target - and we can use getElementById for that.

Collapse
 
codingnninja profile image
Ayobami Ogundiran • Edited

My friend, thanks for bursting my bubble but now, let me also burst your bubble.

You can't use all of the frameworks you mentioned without a transpiler, compiler, virtual DOM or tagged templates.

Now, koras.jsx — $select and $render — makes it possible to use JSX in browsers and servers without a virtual DOM or tagged templates.

Collapse
 
lexlohr profile image
Alex Lohr

You can't use all of the frameworks you mentioned without a transpiler, compiler, virtual DOM or tagged templates.

While that is not entirely true, the other solutions are far less convenient. But even then, what is the issue with that? What should stop me from using these solutions?

Now, koras.jsx — $select and $render — makes it possible to use JSX in browsers and servers without a virtual DOM or tagged templates.

What you have there is not JSX. It is just HTML template strings that look like lit-html in poor light and a renderer that re-renders already rendered components. But we do not re-render full components if not necessary in modern frameworks for a reason. If you test your library in the frontend framework benchmark, you'll see why I'm less than excited about that idea.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

Have you benched marked it? Do so maybe you will reconsider your opinion once again.

Thank you for the insight. I will look into everything to see if there is anything I can use to improve the project.

And remember, none of the frameworks you mentioned can comfortably work on both browsers and servers at the same time without a lot of issues. Don't even mention React because I built the project so that my students could easily learn composition and best React practices from js before moving to React.

Thread Thread
 
lexlohr profile image
Alex Lohr

Have you benched marked it? Do so maybe you will reconsider your opinion once again.

I'm not here to do your work for you. I have superficially read through your code and I see no way your framework can even remotely compete with the faster frameworks.

none of the frameworks you mentioned can comfortably work on both browsers and servers at the same time without a lot of issues.

That's what their related metaframeworks (next, nuxt, svelte-kit, solid-start) are for. Do your research before you make your claims, please.

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran

Why do you assume I have not done my research? See, you are currently criticising the fastest UI framework ever created by man.

Cheers!

Thread Thread
 
lexlohr profile image
Alex Lohr

More outlandish claims for a framework that had its first commit on github last November. I'm not buying it. If you really did your research, please present your findings in a reproducable way. I don't see a benchmarks directory in your repo nor can I find a benchmark for your framework anywhere else I looked.

Cheers!

Thread Thread
 
codingnninja profile image
Ayobami Ogundiran • Edited

I actually like the way you communicated your intention. I will work on benchmarks and add them to the docs.

Now, you see, your suggestion has inspired something meaningful. Thank you!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.