This week, Carson Gross announced that there’s going to be a new v4 of HTMX.
Go read the post: The fetch()ening
I’m obviously very excited to see this. I’m full on team hypermedia — I would say REST, but you know 🥳 — and have been using HTMX since day 1 on the new project.
The trouble is, it’s not so new now. We’re coming up for three years. The project is getting moderately big, and we leant into Attribute Inheritance — which I’m very keen on — which is being switched from implicit to explicit in the new HTMX version.
Here’s an example from the v2 docs:
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
</div>
Here, the hx-confirm is inherited, so you don’t have to duplicate it.
When you’ve got many items all sharing the same hx-target or hx-swap or hx-indicator or whatever, this can really save you a lot of noise.
Apparently, it’s a national nightmare that the inheritance here is implicit. Both “powerful & maddening”.
It’s not something that’s been a problem for me. Indeed, I like it. I could complain that Carson is moving my cheese. But No! Get with the program. You have to trust that your dependencies’ maintainers know better than you. (No, you can’t have that extra FilterSet method! 😉)
Besides, explicit is probably better.
HTMX v4 will have an extension to let you maintain the v2 behaviour, but never go off-piste. ⛷️ The last thing I need is to bring someone else on and have them constantly bemused by the non-stock usage.
§
HTMX v2 has the ability to disable implicit inheritance and then opt-in explicitly providing the hx-inherit attribute when you need to use it.
Bar a trivial syntax change, this is basically the v4 proposal. (V4 will use an :inherited modifier instead, but I’m pretty sure my awk skills are still up to making that adjustment straightforward.)
Nonetheless, I need to find all the cases where I’ve used inheritance, and add the missing hx-inherit attributes before I’m going to be able to do that.
This is something of a blocker. One that I was keen to address.
§
Carson has said that HTMX will provide some way of tracking down your implicit inheritance usages.
That’s likely going to be much better than what follows here.
But I needed to get started so…
§
I vendor HTMX, so step 1 is to switch to the non minified version in development.
Then if we find the getAttributeValueWithDisinheritance function:
function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) {
const attributeValue = getAttributeValue(ancestor, attributeName)
const disinherit = getAttributeValue(ancestor, 'hx-disinherit')
var inherit = getAttributeValue(ancestor, 'hx-inherit')
if (initialElement !== ancestor) {
if (htmx.config.disableInheritance) {
if (inherit && (inherit === '*' || inherit.split(' ').indexOf(attributeName) >= 0)) {
return attributeValue
} else {
return null
}
}
if (disinherit && (disinherit === '*' || disinherit.split(' ').indexOf(attributeName) >= 0)) {
return 'unset'
}
}
return attributeValue
}
This function resolves attribute values, taking inheritance into account.
We’re interested in the if (initialElement !== ancestor) block: did my attribute value come from an ancestor? If so, inheritance is in play.
So there we can add a bit of logging:
if (initialElement !== ancestor) {
// Add logging to catch our inheritance usage:
if (attributeValue != null && !inherit) {
console.warn(
"Missing hx-inherit",
attributeName,
attributeValue,
initialElement,
ancestor
);
}
// Code continues.
Dirtiest of all things. But it works.
We check attributeValue otherwise we get every attribute — hx-boost being quite noisy — all the way up the DOM, unless my eyes were deceiving me.
After that, though, we get nice logging of the actually inherited hx- attributes as we hit them.
We add an appropriate hx-inherit attribute at each point, and the logging goes away.
Hey presto, we can start getting ready for HTMX v4 now. In a week or two’s time, we can begin setting htmx.config.disableInheritance as a check that we caught all the cases.
I’m still looking forward to the official tool here, but this’ll do me to begin.