diff --git a/src/htmx.js b/src/htmx.js index efc4a026..f7d6b489 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -1300,7 +1300,7 @@ var htmx = (() => { tasks.unshift(mainSwap); } - if(!this.__trigger(document, "htmx:before:swap", {ctx, tasks})){ + if(!this.__trigger(ctx.sourceElement, "htmx:before:swap", {ctx, tasks})){ return } @@ -1326,7 +1326,7 @@ var htmx = (() => { await Promise.all(swapPromises); - this.__trigger(document, "htmx:after:swap", {ctx}); + this.__trigger(ctx.sourceElement, "htmx:after:swap", {ctx}); if (ctx.title && !mainSwap?.swapSpec?.ignoreTitle) document.title = ctx.title; this.__handleAnchorScroll(ctx); } @@ -1573,7 +1573,7 @@ var htmx = (() => { composed: true, originalTarget: on }); - let target = on.isConnected ? on : document; + let target = on?.isConnected ? on : document; let result = !detail.cancelled && target.dispatchEvent(evt); return result } diff --git a/test/debug.html b/test/debug.html index aeaf3d8d..e3518085 100644 --- a/test/debug.html +++ b/test/debug.html @@ -83,7 +83,7 @@ - + + diff --git a/test/tests/end2end/events.js b/test/tests/end2end/events.js new file mode 100644 index 00000000..5d9fb28f --- /dev/null +++ b/test/tests/end2end/events.js @@ -0,0 +1,91 @@ +describe('htmx events', function() { + + beforeEach(function() { + setupTest(); + }); + + afterEach(function() { + cleanupTest(); + }); + + it('htmx:before:request fires on sourceElement', async function () { + mockResponse('GET', '/test', 'Response') + let div = createProcessedHTML('
') + let firedOnSource = false + div.addEventListener('htmx:before:request', () => firedOnSource = true) + div.click(); + await forRequest() + assert.isTrue(firedOnSource) + }) + + it('htmx:after:request fires on sourceElement', async function () { + mockResponse('GET', '/test', 'Response') + let div = createProcessedHTML('') + let firedOnSource = false + div.addEventListener('htmx:after:request', () => firedOnSource = true) + div.click() + await forRequest() + assert.isTrue(firedOnSource) + }) + + it('htmx:before:swap fires on sourceElement', async function () { + mockResponse('GET', '/test', 'Response') + let div = createProcessedHTML('') + let firedOnSource = false + div.addEventListener('htmx:before:swap', () => firedOnSource = true) + div.click() + await forRequest() + assert.isTrue(firedOnSource) + }) + + it('htmx:after:swap fires on sourceElement', async function () { + mockResponse('GET', '/test', 'Response') + let div = createProcessedHTML('') + let firedOnSource = false + div.addEventListener('htmx:after:swap', () => firedOnSource = true) + div.click() + await forRequest() + assert.isTrue(firedOnSource) + }) + + it('htmx:after:swap does not fire on element removed from DOM by swap', async function () { + mockResponse('GET', '/test', 'Response') + let div = createProcessedHTML('') + let firedOnSource = false + div.addEventListener('htmx:after:swap', () => firedOnSource = true) + div.click() + await forRequest() + assert.isFalse(firedOnSource) + }) + + it('htmx:after:swap triggers on document when element is removed from DOM by swap', async function () { + mockResponse('GET', '/test', 'Response') + let div = createProcessedHTML('') + let firedOnDocument = false + document.addEventListener('htmx:after:swap', (e) => { + if (e.target === document) { + firedOnDocument = true + } + }, {once: true}) + div.click() + await forRequest() + assert.isTrue(firedOnDocument) + }) + + + it('events bubble from sourceElement to document', async function () { + mockResponse('GET', '/test', 'Response') + let div = createProcessedHTML('') + let bubbledToDocument = false + document.addEventListener('htmx:before:request', (e) => { + if (e.target === div) { + bubbledToDocument = true + } + }, {once: true}) + div.click() + await forRequest() + assert.isTrue(bubbledToDocument) + }) + + // TODO - verify shape of details in these events +})