Skip to content

Commit bd442b5

Browse files
authored
fix: remove empty class attribute left behind after settle/swap/request (#3715)
fix: remove empty class attribute left behind after htmx operations After htmx removes its utility classes (swapping, settling, request), elements are left with an empty class="" attribute if they had no other classes. This happens because doSettle and the request indicator cleanup use raw classList.remove instead of removeClassFromElement, which already handles empty class attribute removal. Replace classList.remove with removeClassFromElement at three sites: - swappingClass removal (line 1978) - settlingClass removal in doSettle (line 1999) - requestClass removal from indicators (line 3415) Fixes #1701 Supersedes #2683
1 parent a706897 commit bd442b5

2 files changed

Lines changed: 74 additions & 3 deletions

File tree

src/htmx.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,7 +1975,7 @@ var htmx = (function() {
19751975
}
19761976
}
19771977

1978-
target.classList.remove(htmx.config.swappingClass)
1978+
removeClassFromElement(target, htmx.config.swappingClass)
19791979
forEach(settleInfo.elts, function(elt) {
19801980
if (elt.classList) {
19811981
elt.classList.add(htmx.config.settlingClass)
@@ -1996,7 +1996,7 @@ var htmx = (function() {
19961996
})
19971997
forEach(settleInfo.elts, function(elt) {
19981998
if (elt.classList) {
1999-
elt.classList.remove(htmx.config.settlingClass)
1999+
removeClassFromElement(elt, htmx.config.settlingClass)
20002000
}
20012001
triggerEvent(elt, 'htmx:afterSettle', swapOptions.eventInfo)
20022002
})
@@ -3412,7 +3412,7 @@ var htmx = (function() {
34123412
forEach(indicators, function(ic) {
34133413
const internalData = getInternalData(ic)
34143414
if (internalData.requestCount === 0) {
3415-
ic.classList.remove.call(ic.classList, htmx.config.requestClass)
3415+
removeClassFromElement(ic, htmx.config.requestClass)
34163416
}
34173417
})
34183418
forEach(disabled, function(disabledElement) {

test/core/class-cleanup.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
describe('Class attribute cleanup after htmx operations', function() {
2+
beforeEach(function() {
3+
this.server = makeServer()
4+
clearWorkArea()
5+
})
6+
afterEach(function() {
7+
this.server.restore()
8+
clearWorkArea()
9+
})
10+
11+
it('removes empty class attribute after settling class is removed', function() {
12+
this.server.respondWith('GET', '/test', 'Settled!')
13+
14+
var div = make('<div hx-get="/test">Click Me!</div>')
15+
// Element starts with no class attribute
16+
should.not.exist(div.getAttribute('class'))
17+
18+
div.click()
19+
this.server.respond()
20+
21+
div.innerHTML.should.equal('Settled!')
22+
// After settle, the settling class should be gone and no empty class="" left behind
23+
should.not.exist(div.getAttribute('class'))
24+
})
25+
26+
it('removes empty class attribute after swapping class is removed', function() {
27+
this.server.respondWith('GET', '/test', '<div id="target">Swapped!</div>')
28+
29+
var div = make('<div id="target" hx-get="/test" hx-swap="outerHTML">Click Me!</div>')
30+
should.not.exist(div.getAttribute('class'))
31+
32+
div.click()
33+
this.server.respond()
34+
35+
var result = byId('target')
36+
result.innerHTML.should.equal('Swapped!')
37+
should.not.exist(result.getAttribute('class'))
38+
})
39+
40+
it('preserves existing classes and only removes htmx utility classes', function() {
41+
this.server.respondWith('GET', '/test', 'Done!')
42+
43+
var div = make('<div class="my-class" hx-get="/test">Click Me!</div>')
44+
div.getAttribute('class').should.equal('my-class')
45+
46+
div.click()
47+
this.server.respond()
48+
49+
div.innerHTML.should.equal('Done!')
50+
// Original class should remain, htmx utility classes should not
51+
div.getAttribute('class').should.equal('my-class')
52+
})
53+
54+
it('removes empty class attribute from indicators after request completes', function() {
55+
this.server.respondWith('GET', '/test', 'Done!')
56+
57+
var indicator = make('<div id="ind"></div>')
58+
var btn = make('<button hx-get="/test" hx-indicator="#ind">Click Me!</button>')
59+
60+
should.not.exist(indicator.getAttribute('class'))
61+
62+
btn.click()
63+
// During request, indicator should have the request class
64+
indicator.classList.contains(htmx.config.requestClass).should.equal(true)
65+
66+
this.server.respond()
67+
68+
// After request, indicator should not have an empty class attribute
69+
should.not.exist(indicator.getAttribute('class'))
70+
})
71+
})

0 commit comments

Comments
 (0)