Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Javascript has become so nice, now that browsers support modules.

I use a module "dqs.js" for all my query needs. It contains only these two lines:

    export const dqs  = document.querySelector.bind(document);
    export const dqsA = document.querySelectorAll.bind(document);
And use it like this:

    import { dqsA } from 'lib/dqs.js';

    for (const rabbit of dqsA('#rabbits .green')) {
        ... do something with all green rabbits ...
    }


I add the parent element to mine

    const $ = (selector, parent = document) => parent.querySelector(selector);
    const $$ = (selector, parent = document) => parent.querySelectorAll(selector);


This is the way. In addition to `$` and `$$`, I usually have `$h` for creating elements:

    /**
     * @param {string} name
     * @param {HTMLElement.prototype} props
     * @param {Array<HTMLElement|string>} children
     * @return HTMLElement
     */
    function $h(name = 'div', props = {}, children = []) {
        const el = document.createElement(name);
        Object.assign(el, props);
        el.append(...children);
        return el;
    }


There is .forEach() on NodeLists, so you can do stuff like this in every browser, no lib, no helper functions needed:

    document.querySelectorAll('a').forEach(tag => {
      // operate on tag
    })


I think it's just for brevity, rather than because a helper is needed. OP listed the entire source code of their module in their comment.


Brevity is cool, but that's kind of like naming all your variables one letter chars for brevity.

This is something every web dev will instantly know what it does:

    document.querySelectorAll('#rabbit .green').forEach(tag => {
      
    })
Whereas this is something nobody knows what the fuck it is because it's completely non-standard.

    for (const rabbit of dqsA('#rabbits .green')) {

    }
And get what, 10 characters less in one row? Basically nothing.


It’s no more ‘non-standard’ than jQuery’s `$`.

Most IDEs will allow you to see what dqsA aliases by mousing over. I don’t really see a problem with it.

It also looks like the kind of thing a pre-commit hook could trivially expand if you’re working with other people who might find it in some way unacceptable.


for/of vs .forEach isn't the issue here. Frankly I almost always use for/of because it's implemented for all iterators and I prefer being outside of a function (e.g. I can use await).

All they did was alias `dqsA`. If anything, it's the fn name that could be better.


alternatively, if you need to use anything other than `forEach` for working on the list, you can turn the NodeList into an array using the array spread syntax (equivalent to using Array.from) e.g.

  [...document.querySelectorAll('a')]
  .filter(node => node.getAttribute("data-foo") === "bar")


Not everyone wants to be so wasteful.


what do you mean by "wasteful"?


Creating an array, populating it with however many elements just to be able to call some methods and destroying it immediately afterwards. And doing it all over the app, just because it's syntactically convenient. (all while you already have an iterable collection in the form of NodeList)


It is a bit weird to consume an iterator into an array when a NodeList is already iterable.

Not sure why it caught on.


I'm also one of those guys that will always spread NodeLists (and other iterable-but-not-array type of objects) but it's really only because I find functional style a lot more readable than imperative style for loops. It might be iterable, but if it doesn't have map/filter/reduce and friends, might as well not be for me.


NodeList.prototype.filter = Array.prototype.filter;

and you can querySelectorAll('...').filter(el => ...) without any copies.

ditto for other mentioned functions. Prototypal nature of Javascript is there for a reason.


In every single project or company I've ever worked on/with, monkey patching was _extremely_ frowned upon (for good reasons). Also, how would you be able to filter without any copies? you'd at least be forced to make one. filter is _not_ supposed to mutate anything. so you'd have to create a new Array and/or NodeList, and put the filtered values back in. Even then, doing all this stuff for what is effectively one single copy that is likely to be insignificant at a performance level is, in my opinion, excessive. I just copy once and make the code clearer.


I see it a lot with other iterables, too. People may like one liners and functional style more than a procedural `for (of)` construct, so they use `[...iterable].any_array_function(...)` everywhere, except when foced not to by async code :).

Might also have something to do with Redux and immutable patterns. (the use of spread operator in general)


Usually it’s because you want to use an array function like .filter() that isn’t implemented by the iterable itself.


NodeList.prototype.filter = Array.prototype.filter;

and you can querySelectorAll('...').filter(el => ...) without any copies.


how's browser support for something like "a:has(img[src$='.gif'])" though?


You probably shouldn't be making that query, unless you're doing something specific like web scraping and don't have control over the content of the site.


That's always been the blessing and curse with JQuery. It allows you to easily filter objects but it doesn't encourage efficiency.

A lot of people don't have control over the content of their site in enterprise situations. If you're stuck using an old framework or CMS you could be beholden to someone else. And at the same time there's a lot of devs who dgaf and just ship what works.


you could be using it already w/jquery though and if you just switched to the native selector it would stop working everywhere but safari.

"#some_combo:has(option:selected[value=..]) + .." seems like a reasonable way to conditionally target something to me, is it terribly worse than some other way?


> is it terribly worse than some other way?

Yeah, it's fragile and will easily lead to bugs when someone changes the markup without realizing it's going to break some crazy selector in another part of the code.

It would make a lot more sense to just add a class to the element you're trying to select.


That selector does not seem fragile at all. It selects any anchor elements with gifs in them. A lot of the time we can't change the markup, either.


it's a conditional select, you're saying just add code to add/remove a class to the target - of course, but that defeats the point of wanting a conditional selector in the first place


The point of selecting an element is to do something with it. How does selecting it by class defeat the point?

Look, if you need this for some one off thing and you've determined it's the best way to do it in this special case, it's not hard to create a function that will find the element you want.

It's not a good argument for using jQuery IMO, because if you're doing this regularly there's probably a better way to do it.

But coming soon™ you will be able to do even this with `document.querySelector`.

Edit: I didn't pay close attention to your second example. I was speaking mostly related to the a:has example before. Your second example seems to be something that would be desired more in CSS than JS, and I don't think it's unreasonable to do that in CSS. If you need to do it in JS you can workaround browser limitations just fine by writing more than one line of code to do the selection and test the condition.


For more advanced queries than standard CSS selectors provide, XPath is also supported by every browser and available via document.evaluate() (which is not eval() by the way)

https://developer.mozilla.org/en-US/docs/Web/XPath/Introduct...


:has doesn't exist but you can do [].map.call(document.querySelectorAll("a img[src$='.gif']"), (e)=>e.closest("a")).filter(e=>e)


:has certainly does exist, but only in Safari. Hopefully Chrome and Firefox will catch up soon.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: