From 4e0c5a260432aa39864a89f848601795ad826bed Mon Sep 17 00:00:00 2001 From: MichaelWest22 Date: Thu, 19 Jun 2025 01:01:08 +1200 Subject: [PATCH 1/2] Add outerHTMLStrip and handle template tag encapsulation --- src/htmx.js | 5 ++++- test/attributes/hx-swap-oob.js | 20 ++++++++++++++++++++ www/content/attributes/hx-swap-oob.md | 23 ++++++++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/htmx.js b/src/htmx.js index bbe83cec9..3682cd235 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -1478,8 +1478,11 @@ var htmx = (function() { fragment = getDocument().createDocumentFragment() fragment.appendChild(oobElementClone) if (!isInlineSwap(swapStyle, target)) { - fragment = asParentNode(oobElementClone) // if this is not an inline swap, we use the content of the node, not the node itself + // @ts-ignore if elt is template content will be valid so use as inner content + fragment = oobElementClone.content || asParentNode(oobElementClone) // if this is not an inline swap, we use the content of the node, not the node itself } + // outerHTMLStrip swapStyle will strip wrapping tag above but do outerHTML swap of inner contents + swapStyle = swapStyle.replace('Strip', '') const beforeSwapDetails = { shouldSwap: true, target, fragment } if (!triggerEvent(target, 'htmx:oobBeforeSwap', beforeSwapDetails)) return diff --git a/test/attributes/hx-swap-oob.js b/test/attributes/hx-swap-oob.js index 875c4d452..c058c58b7 100644 --- a/test/attributes/hx-swap-oob.js +++ b/test/attributes/hx-swap-oob.js @@ -128,6 +128,17 @@ describe('hx-swap-oob attribute', function() { byId('d1').innerHTML.should.equal('Swapped5') }) + it('handles outerHTMLStrip response properly', function() { + this.server.respondWith('GET', '/test', "Clicked
Swapped4.1
") + var div = make('
click me
') + make('
') + div.click() + this.server.respond() + byId('d2').getAttribute('foo').should.equal('bar') + div.innerHTML.should.equal('Clicked') + byId('d2').innerHTML.should.equal('Swapped4.1') + }) + it('oob swaps can be nested in content with config {"allowNestedOobSwaps": true}', function() { htmx.config.allowNestedOobSwaps = true this.server.respondWith('GET', '/test', "
Clicked
Swapped6
") @@ -387,4 +398,13 @@ describe('hx-swap-oob attribute', function() { element.innerHTML.should.equal('Swapped11') }) }) + + it('handles using template as the encapsulating tag of an inner swap', function() { + this.server.respondWith('GET', '/test', 'Swapped12') + var div = make('
click me
') + make('
') + div.click() + this.server.respond() + byId('foo').innerHTML.should.equal('Swapped12') + }) }) diff --git a/www/content/attributes/hx-swap-oob.md b/www/content/attributes/hx-swap-oob.md index 94393fb32..b91a40788 100644 --- a/www/content/attributes/hx-swap-oob.md +++ b/www/content/attributes/hx-swap-oob.md @@ -31,7 +31,7 @@ The value of the `hx-swap-oob` can be: If the value is `true` or `outerHTML` (which are equivalent) the element will be swapped inline. -If a swap value is given, that swap strategy will be used and the encapsulating tag pair will be stripped for all strategies other than `outerHTML`. +If a swap value is given, that swap strategy will be used and the encapsulating tag pair will be stripped for all strategies other than `outerHTML`. There is also an additional swap value `outerHTMLStrip` that still strips the encapsulating tag but allows the replacment the whole target, like `outerHTML`, with all inner content nodes. If a selector is given, all elements matched by that selector will be swapped. If not, the element with an ID matching the new content will be swapped. @@ -71,6 +71,27 @@ A `

` can be encapsulated in `

` or ``: ``` +You can also now use `template` tag as this should work for nearly any tag type: +```html + +``` + +Another new option is the `outerHTMLStrip` swap style that allows you to replace an element with multiple nodes: +```html +
+
+ Replace original +
+
+ And add something more +
+
+``` + ### Troublesome Tables and lists Note that you can use a `template` tag to encapsulate types of elements that, by the HTML spec, can't stand on their own in the From 8a307c77354f7e655bded540eb9d62d7cbff818a Mon Sep 17 00:00:00 2001 From: MichaelWest22 Date: Fri, 20 Jun 2025 10:44:38 +1200 Subject: [PATCH 2/2] use simple if instead --- src/htmx.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/htmx.js b/src/htmx.js index 3682cd235..ed1e33b33 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -1481,8 +1481,9 @@ var htmx = (function() { // @ts-ignore if elt is template content will be valid so use as inner content fragment = oobElementClone.content || asParentNode(oobElementClone) // if this is not an inline swap, we use the content of the node, not the node itself } - // outerHTMLStrip swapStyle will strip wrapping tag above but do outerHTML swap of inner contents - swapStyle = swapStyle.replace('Strip', '') + if (swapStyle === 'outerHTMLStrip') { + swapStyle = 'outerHTML' // outerHTMLStrip will strip wrapping tag above but do outerHTML swap of inner contents + } const beforeSwapDetails = { shouldSwap: true, target, fragment } if (!triggerEvent(target, 'htmx:oobBeforeSwap', beforeSwapDetails)) return