From 8e3936b14e4b450128dfde1e227c360da63f05ee Mon Sep 17 00:00:00 2001 From: MichaelWest22 Date: Sat, 19 Jul 2025 01:02:29 +1200 Subject: [PATCH] add template wrapping support to ws extension --- src/ws/test/ext/ws.js | 34 ++++++++++++++++++++++++++++++++++ src/ws/ws.js | 24 ++++++++++++++++++------ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/ws/test/ext/ws.js b/src/ws/test/ext/ws.js index 35bf6c7..cb9c7de 100644 --- a/src/ws/test/ext/ws.js +++ b/src/ws/test/ext/ws.js @@ -233,6 +233,40 @@ describe('web-sockets extension', function() { byId('d2').innerHTML.should.equal('div2') }) + + it('handles message from the server with template wrapping', function() { + var div = make('
div1
div2
table
') + this.tickMock() + + this.socketServer.emit('message', '
replaced
') + + this.tickMock() + byId('d1').innerHTML.should.equal('replaced') + byId('d2').innerHTML.should.equal('div2') + byId('r3').innerHTML.should.equal('replaced') + }) + + it('handles replacing template with id', function() { + var div = make('
') + this.tickMock() + + this.socketServer.emit('message', '') + + this.tickMock() + byId('t1').innerHTML.should.equal('replaced') + }) + + it('handles replacing template with hx-swap-oob attribute', function() { + var div = make('
') + this.tickMock() + + this.socketServer.emit('message', '') + + this.tickMock() + + div.firstChild.innerHTML.should.equal('replaced') + }) + it('raises lifecycle events (connecting, open, close) in correct order', function() { var handledEventTypes = [] var handler = function(evt) { handledEventTypes.push(evt.detail.event.type) } diff --git a/src/ws/ws.js b/src/ws/ws.js index 9ab33bd..a77c3cb 100644 --- a/src/ws/ws.js +++ b/src/ws/ws.js @@ -78,6 +78,23 @@ This extension adds support for WebSockets to htmx. See /www/extensions/ws.md f } } + /** + * perform oobSwap on all fragment children and process children wrapped in empty template tags + * @param {HTMLCollection} children + * @param {Object} settleInfo + * @returns + */ + function oobSwapChildren(children, settleInfo) { + for (const child of Array.from(children)) { + const hxSwapOob = api.getAttributeValue(child, 'hx-swap-oob') + if(child.tagName !== 'TEMPLATE' || hxSwapOob || child.id) { + api.oobSwap(hxSwapOob || 'true', child, settleInfo) + } else { + oobSwapChildren(child.content.children, settleInfo) + } + } + } + /** * ensureWebSocket creates a new WebSocket on the designated element, using * the element's "ws-connect" attribute. @@ -137,12 +154,7 @@ This extension adds support for WebSockets to htmx. See /www/extensions/ws.md f var settleInfo = api.makeSettleInfo(socketElt) var fragment = api.makeFragment(response) - if (fragment.children.length) { - var children = Array.from(fragment.children) - for (var i = 0; i < children.length; i++) { - api.oobSwap(api.getAttributeValue(children[i], 'hx-swap-oob') || 'true', children[i], settleInfo) - } - } + oobSwapChildren(fragment.children, settleInfo) api.settleImmediately(settleInfo.tasks) api.triggerEvent(socketElt, 'htmx:wsAfterMessage', { message: response, socketWrapper: socketWrapper.publicInterface })