Skip to content

Commit 90d2aed

Browse files
committed
throw targetError correctly when target invalid during retarget
1 parent 5b4d77d commit 90d2aed

3 files changed

Lines changed: 87 additions & 16 deletions

File tree

src/htmx.js

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4716,20 +4716,20 @@ var htmx = (function() {
47164716
}
47174717

47184718
/**
4719-
* Updates the responseInfo's target property if an HX-Retarget header is present
4720-
*
4721-
* @param {XMLHttpRequest} xhr
4722-
* @param {HtmxResponseInfo} responseInfo
4719+
* Resove the Retarget selector and throw if not found
47234720
* @param {Element} elt
4721+
* @param {String} target
47244722
*/
4725-
function handleRetargetHeader(xhr, responseInfo, elt) {
4726-
if (hasHeader(xhr, /HX-Retarget:/i)) {
4727-
if (xhr.getResponseHeader('HX-Retarget') === 'this') {
4728-
responseInfo.target = elt
4729-
} else {
4730-
responseInfo.target = asElement(querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget')))
4731-
}
4723+
function resolveRetarget(elt, target) {
4724+
if (target === 'this') {
4725+
return elt
4726+
}
4727+
const resolvedTarget = asElement(querySelectorExt(elt, target))
4728+
if (resolvedTarget == null) {
4729+
triggerErrorEvent(elt, 'htmx:targetError', { target })
4730+
throw new Error(`Invalid re-target ${target}`)
47324731
}
4732+
return resolvedTarget
47334733
}
47344734

47354735
/**
@@ -4780,9 +4780,6 @@ var htmx = (function() {
47804780
return
47814781
}
47824782

4783-
// handle retargeting before determining history updates/resolving response handling
4784-
handleRetargetHeader(xhr, responseInfo, elt)
4785-
47864783
const historyUpdate = determineHistoryUpdates(elt, responseInfo)
47874784

47884785
const responseHandling = resolveResponseHandling(xhr)
@@ -4791,15 +4788,17 @@ var htmx = (function() {
47914788
let ignoreTitle = htmx.config.ignoreTitle || responseHandling.ignoreTitle
47924789
let selectOverride = responseHandling.select
47934790
if (responseHandling.target) {
4794-
responseInfo.target = asElement(querySelectorExt(elt, responseHandling.target))
4791+
responseInfo.target = resolveRetarget(elt, responseHandling.target)
47954792
}
47964793
var swapOverride = etc.swapOverride
47974794
if (swapOverride == null && responseHandling.swapOverride) {
47984795
swapOverride = responseHandling.swapOverride
47994796
}
48004797

48014798
// response headers override response handling config
4802-
handleRetargetHeader(xhr, responseInfo, elt)
4799+
if (hasHeader(xhr, /HX-Retarget:/i)) {
4800+
responseInfo.target = resolveRetarget(elt, xhr.getResponseHeader('HX-Retarget'))
4801+
}
48034802

48044803
if (hasHeader(xhr, /HX-Reswap:/i)) {
48054804
swapOverride = xhr.getResponseHeader('HX-Reswap')

test/core/config.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,58 @@ describe('htmx config test', function() {
139139
}
140140
})
141141

142+
it('throws targetError if you the target in responseHandling is invalid', function() {
143+
var originalResponseHandling = htmx.config.responseHandling
144+
try {
145+
var error = false
146+
var handler = htmx.on('htmx:targetError', function(evt) {
147+
evt.detail.target.should.equal('#a-div')
148+
error = true
149+
})
150+
htmx.config.responseHandling = originalResponseHandling.slice()
151+
htmx.config.responseHandling.unshift({ code: '444', swap: true, target: '#a-div' })
152+
153+
var responseCode = null
154+
this.server.respondWith('GET', '/test', function(xhr, id) {
155+
xhr.respond(responseCode, { 'Content-Type': 'text/html' }, '' + responseCode)
156+
})
157+
158+
responseCode = 444
159+
var btn = make('<button hx-get="/test">Click Me!</button>')
160+
btn.click()
161+
this.server.respond()
162+
btn.innerHTML.should.equal('Click Me!')
163+
} catch (e) {
164+
} finally {
165+
htmx.config.responseHandling = originalResponseHandling
166+
htmx.off('htmx:targetError', handler)
167+
error.should.equal(true)
168+
}
169+
})
170+
171+
it('can change the target to "this" in a given response code', function() {
172+
var originalResponseHandling = htmx.config.responseHandling
173+
try {
174+
htmx.config.responseHandling = originalResponseHandling.slice()
175+
htmx.config.responseHandling.unshift({ code: '444', swap: true, target: 'this' })
176+
177+
var responseCode = null
178+
this.server.respondWith('GET', '/test', function(xhr, id) {
179+
xhr.respond(responseCode, { 'Content-Type': 'text/html' }, '' + responseCode)
180+
})
181+
182+
responseCode = 444
183+
var div = make('<div id="a-div">Another Div</div>')
184+
var btn = make('<button hx-target="#a-div" hx-get="/test">Click Me!</button>')
185+
btn.click()
186+
this.server.respond()
187+
btn.innerHTML.should.equal('444')
188+
div.innerHTML.should.equal('Another Div')
189+
} finally {
190+
htmx.config.responseHandling = originalResponseHandling
191+
}
192+
})
193+
142194
it('can change the swap type of a given response code', function() {
143195
var originalResponseHandling = htmx.config.responseHandling
144196
try {

test/core/headers.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,26 @@ describe('Core htmx AJAX headers', function() {
280280
div2.innerHTML.should.equal('')
281281
})
282282

283+
it('should handle report target:error when HX-Retarget invalid', function() {
284+
try {
285+
var error = false
286+
var handler = htmx.on('htmx:targetError', function(evt) {
287+
evt.detail.target.should.equal('#d2')
288+
error = true
289+
})
290+
this.server.respondWith('GET', '/test', [200, { 'HX-Retarget': '#d2' }, 'Result'])
291+
292+
var div1 = make('<div id="d1" hx-get="/test"></div>')
293+
div1.click()
294+
this.server.respond()
295+
} catch (e) {
296+
} finally {
297+
htmx.off('htmx:targetError', handler)
298+
div1.innerHTML.should.equal('')
299+
error.should.equal(true)
300+
}
301+
})
302+
283303
it('should handle HX-Reswap', function() {
284304
this.server.respondWith('GET', '/test', [200, { 'HX-Reswap': 'innerHTML' }, 'Result'])
285305

0 commit comments

Comments
 (0)