multi-swap vs select-oob
This is not an issue with the code so to speak. If we need to replace some specific elements there is 2 solutions showcased in the documentation :
select-oob that allow the user to select specific element (that support the extra syntax with : and ,
<button ... hx-select-oob="#posts:beforeend,#more:outerHTML">
But there is also an extension multi-swap that seems to do the exact same thing. The documentation compares it to hx-swap-oob but I don't see the difference with hx-select-oob. It could be interesting to clarify the use case for both methods.
multi-swap traverses the DOM tree. OOB only does top level. That seems to be stated in the multi-swap documentation.
To clarify a bit, top level means that if you return a bunch of fragments that will be swapped into different places, they all need to be in the top-level of the response - thus its not necessarily a "properly formed" html document so much as a bunch of fragments appended consecutively?
Whereas with multi-swap, you could return an actual (say, cached) html page with any degree of nesting, and it will find the fragments wherever they might be and swap them into wherever was designated?
Would it stand to reason then that multi-swap, while more flexible, is also slower than oob swap? Though is the performance difference typically even perceptible?
I'd love to see description of this in documentation for hx-select-oob. It'd save me from a huge headache. I just spent 2 days debugging this behavior of hx-select-oob. I've been happily oob-swapping-in the element with hx-get which was supposed to inherit some properties from already existing parent elements. All looked good in Firefox's DOM inspector, but displaying properties of swapped-in elements showed that they have no parent nodes and HTMX's inheritance mechanism couldn't find any properties to inherit.
multi-swap traverses the DOM tree. OOB only does top level. That seems to be stated in the multi-swap documentation.
I'm confused, since I thought that was the case until a couple of weeks ago. By now I tried to just use hx-select-oob for nested swaps, and it seems to be just working.
Also I don't see a restriction to top-level oob swaps in the code. Here for example is some of the code related to hx-select-oob
// select-oob swaps
if (swapOptions.selectOOB) {
const oobSelectValues = swapOptions.selectOOB.split(',')
for (let i = 0; i < oobSelectValues.length; i++) {
const oobSelectValue = oobSelectValues[i].split(':', 2)
let id = oobSelectValue[0].trim()
if (id.indexOf('#') === 0) {
id = id.substring(1)
}
const oobValue = oobSelectValue[1] || 'true'
const oobElement = fragment.querySelector('#' + id)
if (oobElement) {
oobSwap(oobValue, oobElement, settleInfo)
}
}
}
So just a querySelector and no restriction to parent == null or something.
There is a config somewhat related to that htmx.config.allowNestedOobSwaps, see https://htmx.org/attributes/hx-swap-oob/. Here the related code for hx-swap-oob
/**
* @param {DocumentFragment} fragment
* @param {HtmxSettleInfo} settleInfo
*/
function findAndSwapOobElements(fragment, settleInfo) {
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function (oobElement) {
if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
if (oobValue != null) {
oobSwap(oobValue, oobElement, settleInfo)
}
} else {
oobElement.removeAttribute('hx-swap-oob')
oobElement.removeAttribute('data-hx-swap-oob')
}
})
}
Also here it is not just possible to have nested oob swaps, it is enabled by default (allowNestedSwaps is true by default).
Apparently this got changed in 1.0.2 (see https://github.com/bigskysoftware/htmx/blob/master/CHANGELOG.md#102---2020-12-12), and before that it was
function handleOutOfBandSwaps(fragment, settleInfo) {
forEach(toArray(fragment.children), function (child) {
var oobValue = getAttributeValue(child, "hx-swap-oob");
if (oobValue != null) {
oobSwap(oobValue, child, settleInfo);
}
});
}
which would explain where the "only top level hx-swap-oob" comes from.
I can't see anything right now that multi-swap now can do that hx-select-oob can't. But I the most tired right now than I've been in months, so maybe it's just me... So if anyone could chime in on this, then I would like to put something in the docs.
We realized this 3 years later, that OOB swap could totally handle nested elements, see #2119 I could have sworn I had changed the multiswap docs along back then to remove that note, but it seems it was all just a dream!
We certainly should update the documentation to avoid confusion about what OOB can and cannot do
If you're interested, please feel free to open a documentation PR!