bigskysoftware / htmx

</> htmx - high power tools for HTML
https://htmx.org
Other
37.65k stars 1.27k forks source link

Support swapping classes from response #2103

Open brutella opened 9 months ago

brutella commented 9 months ago

Hi, I'm looking for a way to add and remove node classes from an HTML response. More specifically I wan to add and remove the disabled classes to enable and disable specific buttons on a page. I've discovered the class-tools extension with does swap classes. But this extension doesn't process HTML responses in a way that I would expect.

Consider the following example where there are 2 buttons: Start and Stop. By default the Stop button is disabled by adding the disabled class.

<div hx-ext="class-tools">
    <button id="start" hx-post="/start">Run</button>
    <button id="stop" class="disabled" hx-post="/stop">Stop</button>
</div>

When calling POST /start, I start a task on the server and send a response back to disable the Start button, and enable the Stop button.

<button id="start" classes="add disabled"></button>
<button id="stop" classes="remove disabled"></button>

This doesn't work as expected because the Start and Stop buttons are swapped in from the response resulting in empty buttons. Of course I could send back the full representations of the buttons, but in more complex scenarios a button might consist of an icon and a text. It would be much easier to just swap in classes as needed.


By implementing the handleSwap function in the class-tools extension, I'm able to make it work as expected.

htmx.defineExtension('class-tools', {
    handleSwap: function(swapStyle, target, fragment, settleInfo) {
        htmx.findAll(fragment, '[classes], [data-classes]').forEach(function (elt) {
            var classList = elt.getAttribute("classes") || elt.getAttribute("data-classes");
            if (classList && elt.id) {
                var target = htmx.find("#" + elt.id);
                processClassList(target, classList);
            }
        });
        return true;
    },
    onEvent: function (name, evt) {...}
});

Is this change reasonable and should this be added to the official class-tools extension?

brutella commented 5 months ago

I've updated the handleSwap function as follows and it works great so far.

htmx.defineExtension('class-tools', {
    handleSwap: function(swapStyle, target, fragment, settleInfo) {
        var elts = htmx.findAll(fragment, '[classes], [data-classes]');
        elts.forEach(function (elt) {
            var classList = elt.getAttribute("classes") || elt.getAttribute("data-classes");
            if (classList && elt.id) {
                var target = htmx.find("#" + elt.id);
                if (target) {
                    processClassList(target, classList);
                }
            }
        });
        return elts.length > 0;
    },
    onEvent: function (name, evt) {...}
});

Any change to get this change integrated into the official class-tools extension?

schungx commented 5 months ago

Try also my PR #2485

I have the exact problem and solves it by extending class-tools.

But your solution of doing it during swap is also sound, except that you will destroy the original behavior, I suppose?

My solution does not require knowing the structure of the element to manipulate its classes.