Skip to content

Commit a3a375c

Browse files
authored
Namespace hx-head lifecycle events (#3938)
1 parent 72a0df7 commit a3a375c

4 files changed

Lines changed: 46 additions & 33 deletions

File tree

src/editors/jetbrains/htmx.web-types.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -621,24 +621,24 @@
621621
"doc-url": "https://four.htmx.org/extensions/hx-download#events"
622622
},
623623
{
624-
"name": "before:head:merge",
624+
"name": "head:before:merge",
625625
"description": "Fires before the hx-head extension processes response `<head>` content. `detail.ctx` is the request context.",
626-
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxbeforeheadmerge"
626+
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxheadbeforemerge"
627627
},
628628
{
629-
"name": "before:head:add",
629+
"name": "head:before:add",
630630
"description": "Fires before the hx-head extension adds a new head element. `detail.headElement` is the element. Cancel to skip it.",
631-
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxbeforeheadadd"
631+
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxheadbeforeadd"
632632
},
633633
{
634-
"name": "before:head:remove",
634+
"name": "head:before:remove",
635635
"description": "Fires before the hx-head extension removes a head element. `detail.headElement` is the element. Cancel to keep it.",
636-
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxbeforeheadremove"
636+
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxheadbeforeremove"
637637
},
638638
{
639-
"name": "after:head:merge",
639+
"name": "head:after:merge",
640640
"description": "Fires after the hx-head extension merges head content. `detail.added`, `detail.kept`, and `detail.removed` list affected elements.",
641-
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxafterheadmerge"
641+
"doc-url": "https://four.htmx.org/extensions/hx-head#htmxheadaftermerge"
642642
},
643643
{
644644
"name": "history:cache:before:save",

src/ext/hx-head.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
}
101101
} else {
102102
// if this is a merge, we remove this content since it is not in the new head
103-
if (htmx.trigger(document.body, "htmx:before:head:remove", {headElement: currentHeadElt}) !== false) {
103+
if (htmx.trigger(document.body, "htmx:head:before:remove", {headElement: currentHeadElt}) !== false) {
104104
removed.push(currentHeadElt)
105105
}
106106
}
@@ -124,7 +124,7 @@
124124
newNode._preloadHint = hint
125125
}
126126
} else {
127-
if (htmx.trigger(document.body, "htmx:before:head:add", {headElement: newNode}) !== false) {
127+
if (htmx.trigger(document.body, "htmx:head:before:add", {headElement: newNode}) !== false) {
128128
await appendNode(newNode)
129129
added.push(newNode)
130130
}
@@ -134,12 +134,12 @@
134134
// remove all removed elements, after we have appended the new elements to avoid
135135
// additional network requests for things like style sheets
136136
for (const removedElement of removed) {
137-
if (htmx.trigger(document.body, "htmx:before:head:remove", {headElement: removedElement}) !== false) {
137+
if (htmx.trigger(document.body, "htmx:head:before:remove", {headElement: removedElement}) !== false) {
138138
currentHead.removeChild(removedElement)
139139
}
140140
}
141141

142-
htmx.trigger(document.body, "htmx:after:head:merge", {
142+
htmx.trigger(document.body, "htmx:head:after:merge", {
143143
added: added,
144144
kept: preserved,
145145
removed: removed
@@ -160,7 +160,7 @@
160160
let target = ctx.target
161161
// TODO - is there a better way to handle this? it used to be based on if the element was boosted
162162
let defaultMergeStrategy = target === document.body ? "merge" : "append";
163-
if (htmx.trigger(document.body, "htmx:before:head:merge", detail)) {
163+
if (htmx.trigger(document.body, "htmx:head:before:merge", detail)) {
164164
let realText = ctx.response.raw.text.bind(ctx.response.raw)
165165
ctx.response.raw.text = async () => {
166166
let text = await realText()

test/tests/ext/hx-head.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('hx-head extension', function() {
4040

4141
// Wait for head merge to complete
4242
async function afterMerge() {
43-
await waitForEvent('htmx:after:head:merge', 500)
43+
await waitForEvent('htmx:head:after:merge', 500)
4444
.catch(() => {}); // no head = no event, that's fine
4545
await forRequest();
4646
}
@@ -109,34 +109,34 @@ describe('hx-head extension', function() {
109109
assert.isNotNull(added, 'new element should be added');
110110
});
111111

112-
it('fires htmx:before:head:merge and htmx:after:head:merge events', async function() {
112+
it('fires htmx:head:before:merge and htmx:head:after:merge events', async function() {
113113
let beforeFired = false;
114114
let afterFired = false;
115115

116116
let onBefore = () => { beforeFired = true; };
117117
let onAfter = () => { afterFired = true; };
118-
document.body.addEventListener('htmx:before:head:merge', onBefore);
119-
document.body.addEventListener('htmx:after:head:merge', onAfter);
118+
document.body.addEventListener('htmx:head:before:merge', onBefore);
119+
document.body.addEventListener('htmx:head:after:merge', onAfter);
120120

121121
mockResponse('GET', '/page', headResponse('<meta name="hx-head-test-evt" content="val">', '<div>content</div>'));
122122
let div = createProcessedHTML('<div hx-get="/page" hx-swap="innerHTML">click</div>');
123123

124124
div.click();
125125
await afterMerge();
126126

127-
document.body.removeEventListener('htmx:before:head:merge', onBefore);
128-
document.body.removeEventListener('htmx:after:head:merge', onAfter);
127+
document.body.removeEventListener('htmx:head:before:merge', onBefore);
128+
document.body.removeEventListener('htmx:head:after:merge', onAfter);
129129

130130
let added = document.head.querySelector('meta[name="hx-head-test-evt"]');
131131
if (added) addedHeadElts.push(added);
132132

133-
assert.isTrue(beforeFired, 'htmx:before:head:merge should fire');
134-
assert.isTrue(afterFired, 'htmx:after:head:merge should fire');
133+
assert.isTrue(beforeFired, 'htmx:head:before:merge should fire');
134+
assert.isTrue(afterFired, 'htmx:head:after:merge should fire');
135135
});
136136

137-
it('htmx:before:head:merge cancellation prevents merge', async function() {
137+
it('htmx:head:before:merge cancellation prevents merge', async function() {
138138
let onBefore = (e) => { e.preventDefault(); };
139-
document.body.addEventListener('htmx:before:head:merge', onBefore, {once: true});
139+
document.body.addEventListener('htmx:head:before:merge', onBefore, {once: true});
140140

141141
mockResponse('GET', '/page', headResponse('<meta name="hx-head-test-cancel" content="val">', '<div>content</div>'));
142142
let div = createProcessedHTML('<div hx-get="/page" hx-swap="innerHTML">click</div>');
@@ -181,7 +181,7 @@ describe('hx-head extension', function() {
181181

182182
let onMerge = () => { headMergeTime = Date.now(); };
183183
let onSwap = () => { swapTime = Date.now(); };
184-
document.body.addEventListener('htmx:after:head:merge', onMerge);
184+
document.body.addEventListener('htmx:head:after:merge', onMerge);
185185
document.body.addEventListener('htmx:after:swap', onSwap);
186186

187187
mockResponse('GET', '/page', headResponse('<meta name="hx-head-test-timing" content="val">', '<div>swapped</div>'));
@@ -190,7 +190,7 @@ describe('hx-head extension', function() {
190190
div.click();
191191
await afterMerge();
192192

193-
document.body.removeEventListener('htmx:after:head:merge', onMerge);
193+
document.body.removeEventListener('htmx:head:after:merge', onMerge);
194194
document.body.removeEventListener('htmx:after:swap', onSwap);
195195

196196
let added = document.head.querySelector('meta[name="hx-head-test-timing"]');

www/src/content/extensions/04-hx-head.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -183,24 +183,24 @@ Element events expose:
183183
event.detail.headElement // Element being added or removed
184184
```
185185

186-
### `htmx:before:head:merge`
186+
### `htmx:head:before:merge`
187187

188188
Fires before the extension processes a response `<head>`.
189189

190190
```js
191-
document.addEventListener('htmx:before:head:merge', event => {
191+
document.addEventListener('htmx:head:before:merge', event => {
192192
if (!shouldUpdateHead(event.detail.ctx)) event.preventDefault()
193193
})
194194
```
195195

196196
The event detail includes the request `ctx`. Cancel the event to skip all head processing for the response.
197197

198-
### `htmx:before:head:add`
198+
### `htmx:head:before:add`
199199

200200
Fires before the extension adds a head element.
201201

202202
```js
203-
document.addEventListener('htmx:before:head:add', event => {
203+
document.addEventListener('htmx:head:before:add', event => {
204204
if (event.detail.headElement.matches('script[data-untrusted]')) {
205205
event.preventDefault()
206206
}
@@ -209,12 +209,12 @@ document.addEventListener('htmx:before:head:add', event => {
209209

210210
Cancel the event to skip that element.
211211

212-
### `htmx:before:head:remove`
212+
### `htmx:head:before:remove`
213213

214214
Fires before the extension removes a current `<head>` element.
215215

216216
```js
217-
document.addEventListener('htmx:before:head:remove', event => {
217+
document.addEventListener('htmx:head:before:remove', event => {
218218
if (event.detail.headElement.matches('[data-keep]')) {
219219
event.preventDefault()
220220
}
@@ -223,7 +223,7 @@ document.addEventListener('htmx:before:head:remove', event => {
223223

224224
Cancel the event to keep that element.
225225

226-
### `htmx:after:head:merge`
226+
### `htmx:head:after:merge`
227227

228228
Fires after the extension finishes updating the `<head>`.
229229

@@ -236,11 +236,24 @@ event.detail = {
236236
```
237237

238238
```js
239-
document.addEventListener('htmx:after:head:merge', event => {
239+
document.addEventListener('htmx:head:after:merge', event => {
240240
console.log('Head elements added:', event.detail.added.length)
241241
})
242242
```
243243

244+
## Migration
245+
246+
### Beta to RC1
247+
248+
RC1 namespaces `hx-head` lifecycle events:
249+
250+
| Beta | RC1 |
251+
|------|-----|
252+
| `htmx:before:head:merge` | [`htmx:head:before:merge`](#htmxheadbeforemerge) |
253+
| `htmx:before:head:add` | [`htmx:head:before:add`](#htmxheadbeforeadd) |
254+
| `htmx:before:head:remove` | [`htmx:head:before:remove`](#htmxheadbeforeremove) |
255+
| `htmx:after:head:merge` | [`htmx:head:after:merge`](#htmxheadaftermerge) |
256+
244257
## Notes
245258

246259
- `<style>` applies and `<script>` runs before swap; `<script defer>` runs after:

0 commit comments

Comments
 (0)