Skip to content

Instantly share code, notes, and snippets.

@1cg

1cg/htmx2.md Secret

Last active November 14, 2023 03:48
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1cg/f1a6d44c34195c4b6b30df864cf3dcef to your computer and use it in GitHub Desktop.
Save 1cg/f1a6d44c34195c4b6b30df864cf3dcef to your computer and use it in GitHub Desktop.

Very Likely

To Discuss

  • adopt explicit inheritance syntax:
    <div hx-target:inherit="#main" ... >
       ...
    </div>
  • more elaborate head merging for boosted links (should be doable w/ template-tag parsing & merge)
@gone
Copy link

gone commented Jun 28, 2022

I'd love more events around boosted links as well (like an event to trigger when a boost finshes, to replicate document.ready like init stuff)

@benpate
Copy link

benpate commented Jun 28, 2022

Here’s my (unordered) wish list. I’ll try to reorganize when I’m on a real keyboard.

  • Expand public API so that other scripts (and hyperscript, extensions, etc) can trigger swaps using htmx logic
  • +10 for renaming extensions to plugins and making a plugin registry.
  • Plugin registry should account for semantic versioning
  • Allow extensions to register their own attributes, such as hx-sse or hx-boost
  • Dynamically evaluate most attributes so that external scripts can modify things like hx-swap and hx-target without calling htmx.process(), with hx-trigger As the obvious (and we’ll-documented) exception.
  • Align attributes and response header values, such as hx-target and HX-Retarget. Support additional response headers (like HX-Swap) to drive more behaviors from the server.
  • Separate XHR features from swap features so that they can be called/triggered independently. (stretch goal example: enhance the preload extension to trigger XHR on mousedown and swap on click)
  • Make hx-boost an extension
  • Make a different way for extensions to listen for specific events. if (event.type == “htmx:beforeOnLoad”) feels icky.
  • standardize/simplify attribute arguments. `hx-swap‘ is especially complex
  • Enable hx-confirm and hx-prompt to call configurable external scripts instead of just browser defaults. Consider remaking them as extensions, too.
  • Rename OOB swaps to something more straightforward.
  • Subject hx-vars, hx-vals, hx-include, and hx-params to a sudden elimination death match, preferably following the Highlander rule - there can be only one.

@Renerick
Copy link

Renerick commented Sep 3, 2022

Abolish internal extension API in favor of public API. Simplify extension contract (ideally, a single init method, that initializes events listeners, registers custom attributes, swaps, events, etc)

Basically, an extension should be similar to behaviors in hyperscript, being just an easy declarative way to reuse behavior across different elements. If a developer wants to have custom event/attribute/swapping mechanism/whatever, but does not want to write a full extension, they should be able to do it with public API.

This would require refinement of public API and events in order to allow comprehensive support for as many use cases as possible

@benpate
Copy link

benpate commented Sep 4, 2022

Yes. The “internal api” was a stopgap while we work out what the public api should be. Suggestions on what methods we should include in the new contract?

@Renerick
Copy link

Renerick commented Sep 4, 2022

  1. Helper methods (getAttributeValue, getClosesMatch, mergeObject etc.)
  2. Access to htmx event system (on, off, but with custom htmx events support, like what addEventhHandler provides)
  3. A method, preparing request data from an element (parse form values, headers, hx-vals/vars, filters)
  4. A method, invoking ajax request
  5. A method, that processes the entire response (selecting, swapping, oob-selecting, oob-swapping, settling)
  6. Swap method (that also does settling)
  7. Methods to register custom features (htmx.registerAttribute, htmx.registerSwap, htmx.registerEvent)
  8. ...?

The list above are the most obvious use cases that an (extension) developer might want to do from their scripts, coming from my experience and Discord questions.

An extension then would look like that

var ext = {
    init: function() {
        htmx.registerAttribute('cool-feature', 
            function(elt, value) { 
                htmx.on('click', elt, function(evt) { console.log('Hello world. Attribute value is ' + value) })
            }
        );
        htmx.registerEvent('triple-click', 
            function(elt, params) { 
                elt.addEventListener('click', function (evt) {
                    if (evt.detail === 3) {
                        htmx.triggerEvent('triple-click', elt, {clicks: 3});
                    }
                });
            }
        );
        htmx.registerSwap('idiomorphius', 
            function(target, fragment) { 
                idiomorphius.doMagic(target, fragment);
            }
        );
    }
}

htmx.defineExtension('new-ext', ext)

and that's how htmx primary event listener would look like (oversimplified)

...
htmx.triggerEvent('beforeRequest', elt);
var request = prepareRequest(elt); //returns parameters from form data/hx-vals etc, urls, method
htmx.triggerEvent('configRequest', elt, request);

// listens for htmx:abort internally
htmx.ajax(request.method, request.url, request.body, request.headers, elt, function(response) {

    htmx.triggerEvent('afterResponse', response);
    htmx.processHeaders(response.headers);

    var swapSpec = htmx.getSwappingSpec(elt);
    htmx.triggerEvent('beforeProcessResponse', elt, {swapSpec, response});
    htmx.processResponse(response, swapSpec) //does selecting, oob selecting, oob swapping, swapping, settling. triggers swap, oobSwap and settle related event *every time* they happen

   htmx.triggerEvent('afterProcessResponse', elt);
});

Stuff like parameters encoding and response transformation could be done with event handlers (htmx:configRequest and htmx:afterResponse respectively).

The only tricky thing with this approach is scoping of the extensions, which might require also having onEvent method, like the existing implementation.

All this is obviously open for comments

Rereading @benpate's comment above, many of those points were already made, so props to him for being first!

@benpate
Copy link

benpate commented Sep 4, 2022

Lots of good stuff here! I know that @1cg was trying to accomplish something like public/private methods in the existing htmx API, probably (and sensibly) to avoid making commitments about htmx's internal architecture. But it's seeming more and more like we're asking to publish nearly everything publicly so that extension/integration authors can use pieces of htmx however they need.

It's hard to know exactly how to get started on such a big set of changes (even if we all agree to do them). But perhaps we could map out what the public APIs could look like and use that as a starting point?

@1cg
Copy link
Author

1cg commented Sep 4, 2022

I'm willing to make the bet with 2.0 that we have the internals pretty much settled and won't be dragging it around much, so I'm fine with publishing most of the internal API as public API in the next version, but I want to think really hard about the API. It's been haphazard so far. With the websockets and sse functionality moved out to separate-but-heavily-integrated extensions, we've got some experience and I think @Renerick has the right idea.

Unfortunately this is all going to have to wait for 2.0 work, which I don't anticipate until 2023 due to other stuff I have going on. If you guys want to noodle on it though, please do.

One thought I had: what if most extensions were done through events? Would that simplify things? (e.g. trigger an "htmx:handleSwap" event and see if anything else handled the swap, rather than offering an explicit callback?)

@gone
Copy link

gone commented Sep 18, 2022

I think that would be really powerful - I was thinking EG the history support stuff should be pushed out to it's own extension and using events to drive that would be a clean separation.

@ia3andy
Copy link

ia3andy commented Nov 10, 2022

As discussed on Zulip, inheritance can lead to hard to predict side effect, even more when the page content is spread across different templates/tags. Adopting explicit inheritance would be a great improvement!

This is a upvote for this feature in 2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment