htmx icon indicating copy to clipboard operation
htmx copied to clipboard

[proposal] extend hx-preserve functionality to optionally preserve attibutes and classes only

Open kizmonavt opened this issue 1 year ago • 5 comments

When updating elements on the frontend sometimes we just want to update the data and keep some frontend specific classes/attributes unchanged. Some elements might be highlighted, hidden, or expanded on the frontend and when the update is received we want to preserve classes and attributes that are responsible for this (eg. <div hidden> , <div class="highligted">, <details open>, ...)

hx-preserve currently only supports the preservation of the whole element which means that it can not be used if we want to update the data. We can however extend its functionality by accepting parameters in a following manner: hx-preserve="attr:open" and hx-preserve="class:hidden" This way we can preserve specific attributes or classes.

implementation seems quite simple:

`        function handlePreservedElements(fragment) {
            forEach(findAll(fragment, '[hx-preserve], [data-hx-preserve]'), function (preservedElt) {
                var id = getAttributeValue(preservedElt, "id");
                var oldElt = getDocument().getElementById(id);
                if (oldElt == null){
                    return
                }
                var parameters = getAttributeValue(preservedElt, 'hx-preserve')
                if (parameters){
                    preserveAttributesAndClasses(oldElt, preservedElt, parameters)
                } else {
                    preservedElt.parentNode.replaceChild(oldElt, preservedElt);
                }
            });
        }

        function preserveAttributesAndClasses(oldElt, preservedElt, parameters){
            forEach(parameters.split(","), function (parameter){
                {
                    var splitted_parameter = parameter.split(":")
                    var qualifier = splitted_parameter[0]
                    var name = splitted_parameter[1]
                    if (qualifier === 'attr') {
                        if (oldElt.hasAttribute(name)) {
                            preservedElt.setAttribute(name, oldElt.getAttribute(name))
                        } else {
                            preservedElt.removeAttribute(name)
                        }
                    } else if (qualifier === 'class') {
                        if (oldElt.classList.contains(name)) {
                            preservedElt.classList.add(name)
                        } else {
                            preservedElt.classList.remove(name)
                        }
                    }
                }
            })
        }

kizmonavt avatar Jan 16 '24 07:01 kizmonavt

Wouldn't you be able to use inner swaps when necessary?

Is there a case you can show when it's not possible to work with inner/outer swaps? I'm curious what I'm missing.

andryyy avatar Jan 16 '24 08:01 andryyy

You can have a list of items that gets updated with a single request and therefore swapped simultaneously.

<div hx-get="/accounts"
     hx-trigger="every 10s"
     hx-swap="outerHTML">

  <details id="1">
    <summary>summary1</summary>
    details1
  </details>
  <details id="2" open="">
    <summary>summary2</summary>
    details2
  </details>
</div>

related reddit thread: https://www.reddit.com/r/htmx/comments/196do20/periodically_refresh_a_list_of_items_with_htmx/

kizmonavt avatar Jan 16 '24 09:01 kizmonavt

I agree with @kizmonavt that it would be a useful feature.

svenberkvens avatar Jan 24 '24 07:01 svenberkvens