htmx
htmx copied to clipboard
Rework "find" extended CSS selector
I must admit I'm not very satisfied with the current find keyword
closest seems obvious because it's going to retrieve the closeST element, so obviously only one.
But when it comes to find, maybe is it because I'm not a native English speaker, but I "feel" it differently depending on the context
- For hx-target, I think of it as retrieving a single element (which is correct)
- But for hx-trigger's
fromkeyword, andhx-include, I would expect it to return all elements matching the selector (which is not correct, current implementation only returns the first matching child) Examples for that second point:
<div hx-get="/search" hx-trigger="keyup from:find input[type='search']">
...
<label>Some filter <input type="search"></label>
<label>Some other filter <input type="search"></label>
</div>
Here, I would instinctively expect the div to listen for keyup on both inputs, or more if there were more of them.
Not only does it only retrieve the first matching child, the only way to accomplish such behaviour as of today would be using a standard CSS selector such as #result input[type='search'], which requires adding an ID / class to be able to select on that parent div specifically.
I find it confusing because the word find itself doesn't indicate if it's going to be one, or many elements
So, would it make sense to add a new keyword to make that clearer ? Which would btw add the feature of listening on multiple elements using from as mentioned above
Examples that come to mind:
find-firstandfind-all
<div hx-trigger="from:find-first input"></div>
<div hx-trigger="from:find-all input"></div
find-childandfind-children
<div hx-trigger="from:find-child input"></div>
<div hx-trigger="from:find-children input"></div
closest-childandchildren
<div hx-trigger="from:closest-child input"></div>
<div hx-trigger="from:children input"></div
Feel free to suggest other ideas, especially given that my English isn't that great
For retro-compatibility, I would then suggest to keep find being an alias of whatever new keyword that would replace it, but deprecate it / undocument it
Let me know
Calling find in querySelectorAllExt seems wrong to me, because the function name querySelectorAllExt suggests something else. For next and previous, querySelectorAll is also used. What if we use findAll as a quick fix here? This way you could use :first-child if you are only interested in the first element e.g. from:find input:first-child.
BTW: renaming find to children sounds reasonable to me.
Edit: I was wrong, next and previous return a single item. I would also expect both to return a list of elements.
Considering the following situation:
<div hx-trigger="input from:find input">
<div>Something</div>
<input type="text">
<input type="text">
</div>
With the current find keyword, the parent div woud listen for the input event on the first of the two text inputs.
With your PR though:
- The parent div would now listen on both inputs (that's why I was suggesting a retro compatibility for the
findkeyword as this would change the behaviour of an existing feature) - FYI,
:first-childisn't an equivalent here, as the doc says:
The :first-child CSS pseudo-class represents the first element among a group of sibling elements.
<div hx-trigger="input from:find input:first-child">
<div>Something</div>
<input type="text">
<input type="text">
</div>
Using :first-child, the div wouldn't retrieve any input at all since the first input isn't the first child of its sibling elements group, it's actually the second child
So I'm afraid it wouldn't fix the issue here
I agree with you that next and previous should allow the user to retrieve multiple elements if they want to, but here for retro-compatibility purposes as well, I think we'd better add another keyword to differentiate single element retrieval from multiple elements retrieval
Meh, you're absolutely right. I don't know why I thought it would return the first element. So let's first discuss what approach we want to take here before I adjust the related PR to it.
What if we used a function-like syntax similar to CSS pseudo-classes :has(), :not(), but without the colons? The old syntax will be deprecated and removed in 2.0.
closest(<selector>) (do we need first-closest() and closest() or does it not make sense?)
first-child(<selector>)
children(<selector>)
first-next(<selector>)
next(<selector>)
first-previous(<selector>)
previous(<selector>)
<div hx-trigger="input from:first-child(input[type="submit"])"></div>
I like those ideas
- I think
closestshould return only one element, as it mirrors the standard API If we want a multiple retrieval of parent elements, I don't think the "closest" keyword makes any sense anymore since it would return any parent matching the selector, so there wouldn't be any left notion of proximity, and "closest" implies the nearest one imo For that, maybeparents(<selector>)following your syntax would do the trick - I'm not sure about that function syntax with parentheses instead of the current one that is in the form
clause <selector>I guess it's a matter of taste here, but if I'd have to pick, I think I would stick to the easiest syntax, in the way that just requiring a space between the clause and the selector will eliminate the "I forgot to close my parentheses" potential issues. But again, probably a matter of taste - I'm worried about the potential confusion that users could face between a
first-childclause that wouldn't behave like CSS's:first-child, but maybe am I worrying too much here
I think closest should return only one element
I agree.
I'm not sure about that function syntax with parentheses instead of the current one that is in the form clause
The function syntax feels more natural to me and encloses the selector, as is done in CSS with :has(), etc. But yes, it's a matter of taste. Without we have to rename next() and previous() to something else to be backward compatible.
<div hx-trigger="input from:first-child(input[type="submit"])"></div>
vs.
<div hx-trigger="input from:first-child input[type="submit"]"></div>
I'm worried about the potential confusion that users could face between a first-child clause that wouldn't behave like CSS's :first-child, but maybe am I worrying too much here
Don't worry, the documentation will clarify this :stuck_out_tongue_winking_eye:
Either rename next/previous as you said, but we could also
- not rename at all, thus we'd have to wait for htmx 2 to introduce breaking changes (I must admit I'm not fond of that option though)
- introduce new keywords for the multiple retrieval versions instead, like
next-siblingsorprevious-siblingsor whatever. Again, I'm not a native English speaker so I don't really know ifnext/previous"feels" more single or multiple retrieval at the first glance (though as you said, we could rely on the documentation to clarify that)
When using multiple keywords a one-time legacy name info could be helpful, shown in the console.
I've been looking for a find-all selector. Is there any chance of this happening?
I spent a lot of time trying to troubleshoot my app due to the lack of a this or children selector.
I have two forms with the same set of inputs; one is for mobile and the other is for desktop, with layouts different enough that the markup cannot be combined.
What I would expect to be able to do is put an hx-trigger attribute on a form element that listens only to child inputs, to avoid conflicting with the other duplicate form in the DOM. Using an ID selector works, but doesn't feel as clean as the rest of HTMX's syntax, so being able to accomplish this kind of scoping with this or children would be welcome.