Skip to content

Commit 8b59cb9

Browse files
committed
docs + config of head handling
1 parent 5cdd50e commit 8b59cb9

File tree

4 files changed

+74
-21
lines changed

4 files changed

+74
-21
lines changed

src/htmx.js

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ var htmx = (function() {
6161
disableInheritance: false,
6262
head : {
6363
boost : "merge",
64-
other: "title",
64+
other: "none",
6565
},
6666
responseHandling: [
6767
{ code: '204', swap: false },
@@ -1143,17 +1143,12 @@ var htmx = (function() {
11431143
}
11441144
}
11451145

1146-
function handleHeadTag(head, defaultStrategy) {
1147-
1148-
if (head && htmx.config.head) {
1149-
1150-
if (defaultStrategy === "none") {
1151-
return
1152-
}
1146+
function handleHeadTag(head, strategy) {
11531147

1148+
if (head && (strategy === "merge" || strategy === "append")) {
11541149
// allow new head to override merge strategy
1155-
let elementMergeStrategy = getAttributeValue(head, "hx-head") || defaultStrategy;
1156-
if (elementMergeStrategy === "append" || elementMergeStrategy === "merge") {
1150+
let elementMergeStrategy = getAttributeValue(head, "hx-head") || strategy;
1151+
if (elementMergeStrategy === "merge" || elementMergeStrategy === "append") {
11571152
let removed = []
11581153
let appended = []
11591154

@@ -2216,7 +2211,7 @@ var htmx = (function() {
22162211
const historyElement = getHistoryElement()
22172212
const settleInfo = makeSettleInfo(historyElement)
22182213
handleTitle(fragment.title);
2219-
handleHeadTag(fragment.head, "merge");
2214+
handleHeadTag(fragment.head, htmx.config.head.boost);
22202215

22212216
// @ts-ignore
22222217
swapInnerHTML(historyElement, content, settleInfo)
@@ -2239,7 +2234,7 @@ var htmx = (function() {
22392234
const historyElement = getHistoryElement()
22402235
const settleInfo = makeSettleInfo(historyElement)
22412236
handleTitle(fragment.title);
2242-
handleHeadTag(fragment.head, "merge");
2237+
handleHeadTag(fragment.head, htmx.config.head.boost);
22432238
swapInnerHTML(historyElement, fragment, settleInfo)
22442239
settleImmediately(settleInfo.tasks);
22452240
setTimeout(function() {
@@ -2586,6 +2581,8 @@ var htmx = (function() {
25862581
swapSpec.transition = value.substr(11) === 'true'
25872582
} else if (value.indexOf('ignoreTitle:') === 0) {
25882583
swapSpec.ignoreTitle = value.substr(12) === 'true'
2584+
} else if (value.indexOf('head:') === 0) {
2585+
swapSpec.head = value.substr(5)
25892586
} else if (value.indexOf('scroll:') === 0) {
25902587
const scrollSpec = value.substr(7)
25912588
var splitSpec = scrollSpec.split(':')
@@ -3381,6 +3378,7 @@ var htmx = (function() {
33813378
const shouldSwap = responseHandling.swap
33823379
let isError = !!responseHandling.error
33833380
let ignoreTitle = htmx.config.ignoreTitle || responseHandling.ignoreTitle
3381+
let head = responseInfo.boosted ? htmx.config.head.boost : htmx.config.head.other
33843382
let selectOverride = responseHandling.select
33853383
if (responseHandling.target) {
33863384
responseInfo.target = querySelectorExt(elt, responseHandling.target)
@@ -3408,7 +3406,8 @@ var htmx = (function() {
34083406
serverResponse,
34093407
isError,
34103408
ignoreTitle,
3411-
selectOverride
3409+
selectOverride,
3410+
head
34123411
}, responseInfo)
34133412

34143413
if (responseHandling.event && !triggerEvent(target, responseHandling.event, beforeSwapDetails)) return
@@ -3419,6 +3418,7 @@ var htmx = (function() {
34193418
serverResponse = beforeSwapDetails.serverResponse // allow updating content
34203419
isError = beforeSwapDetails.isError // allow updating error
34213420
ignoreTitle = beforeSwapDetails.ignoreTitle // allow updating ignoring title
3421+
head = beforeSwapDetails.head // allow updating head algorithm
34223422
selectOverride = beforeSwapDetails.selectOverride // allow updating select override
34233423

34243424
responseInfo.target = target // Make updated target available to response events
@@ -3447,6 +3447,9 @@ var htmx = (function() {
34473447
if (swapSpec.hasOwnProperty('ignoreTitle')) {
34483448
ignoreTitle = swapSpec.ignoreTitle
34493449
}
3450+
if (swapSpec.hasOwnProperty('head')) {
3451+
head = swapSpec.head
3452+
}
34503453

34513454
target.classList.add(htmx.config.swappingClass)
34523455

@@ -3524,10 +3527,10 @@ var htmx = (function() {
35243527
handleTitle(settleInfo.title);
35253528
}
35263529

3530+
console.log("Here", head)
35273531
// merge in new head after swap but before settle
35283532
if (triggerEvent(document.body, "htmx:beforeHeadMerge", {head: settleInfo.head})) {
3529-
handleHeadTag(settleInfo.head, responseInfo.boosted ? htmx.config.head.boost :
3530-
htmx.config.head.other);
3533+
handleHeadTag(settleInfo.head, head);
35313534
}
35323535

35333536
if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) {
@@ -3642,8 +3645,9 @@ var htmx = (function() {
36423645
* @param {import("./htmx").HtmxExtension} extension
36433646
*/
36443647
function defineExtension(name, extension) {
3648+
if (name === "head-support") return; // ignore the head support extension, now integrated into htmx
36453649
if (extension.init) {
3646-
extension.init(internalAPI)
3650+
extension.init(internalAPI);
36473651
}
36483652
extensions[name] = mergeObjects(extensionBase(), extension)
36493653
}

test/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<meta http-equiv="expires" content="0" />
1010
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
1111
<meta http-equiv="pragma" content="no-cache" />
12-
<meta name="htmx-config" content='{"historyEnabled":false,"defaultSettleDelay":0,"head":false}'>
12+
<meta name="htmx-config" content='{"historyEnabled":false,"defaultSettleDelay":0,"head":{"boost":"none","other":"none"}}'>
1313
</head>
1414
<body style="padding:20px;font-family: sans-serif">
1515

www/content/attributes/hx-swap.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ If you want to use the new [View Transitions](https://developer.mozilla.org/en-U
3939
when a swap occurs, you can use the `transition:true` option for your swap. You can also enable this feature globally by
4040
setting the `htmx.config.globalViewTransitions` config setting to `true`.
4141

42+
#### `head` tag handling: `head`
43+
44+
If you want to modify how a `head` tag found in the new content is handled, you can use the `head` modifier, with one
45+
of the following values:
46+
47+
* `merge` - merge the new head tag elements into the existing element
48+
* `append` - append the new head tag elements to the existing head tag
49+
* `none` - ignore any new head tag elements
50+
4251
#### Timing: `swap` & `settle`
4352

4453
You can modify the amount of time that htmx will wait after receiving a response to swap the content

www/content/docs.md

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ custom_classes = "wide-content"
2929
* [confirming](#confirming)
3030
* [inheritance](#inheritance)
3131
* [boosting](#boosting)
32+
* [head tag handling](#head)
3233
* [websockets & SSE](#websockets-and-sse)
3334
* [history](#history)
3435
* [requests & responses](#requests)
@@ -182,10 +183,6 @@ To upgrade to htmx 2.0 from htmx 1.0, you will need to do the following:
182183
`htmx.config.methodsThatUseUrlParams` to `["get"]` (it's a little crazy, but `DELETE`, according to the spec, should
183184
use request parameters.)
184185
* If you want to make cross-domain requests with htmx, revert `htmx.config.selfRequestsOnly` to `false`
185-
* If you want to revert these to the htmx 1.x defaults, you can use the following meta tag:
186-
```html
187-
<meta name="htmx-config" content='{"scrollBehavior":"smooth", "methodsThatUseUrlParams":["get"], "selfRequestsOnly": false}'>
188-
```
189186
* Convert any `hx-on` attributes to their `hx-on:` equivalent:
190187
```html
191188
<button hx-get="/info" hx-on="htmx:beforeRequest: alert('Making a request!')
@@ -202,6 +199,9 @@ To upgrade to htmx 2.0 from htmx 1.0, you will need to do the following:
202199
Note that you must use the kebab-case of the event name due to the fact that attributes are case-insensitive in HTML.
203200
```
204201
* The `htmx.makeFragment()` method now **always** returns a `DocumentFragment` rather than either an `Element` or `DocumentFragment`
202+
* If you are using htmx in a module setting, we now provide module-type specific files for all three of the major
203+
JavaScript module types: `/dist/htmx.esm.js`, `/dist/htmx.umd.js` & `/dist/htmx.amd.js`
204+
* htmx 2.0 offers [automatic head merging](#head-support) with boosted links. If you do not want this behavior, set you can set `htmx.config.head.boosted` to `"none"`
205205

206206
IE is no longer supported in htmx 2.0, but htmx 1.x continues to support IE and will be supported for the foreseeable
207207
future.
@@ -779,6 +779,46 @@ Here is an example:
779779

780780
The anchor tag in this div will issue an AJAX `GET` request to `/blog` and swap the response into the `body` tag.
781781

782+
### `head` tag support
783+
784+
In boosted requests, if a head tag is detected in the response, htmx will automatically synchronize the content of
785+
the current head tag with the new content. This means that elements that are already found in the current head will
786+
be left alone (and, therefore, not trigger another request), but new elements found in the new header will be added
787+
to the existing header tag. Elements that are not found in the new head will be removed from the existing head tag.
788+
789+
This allows you to include page-specific header related elements and have them added or removed via boosted requests.
790+
791+
htmx also supports an "append" mode, that will simply append the content of the new head tag to the current head, if
792+
the new content is not already in it. You can control the mode that htmx will use with the `hx-head` attribute on
793+
the head tag in the *new* content:
794+
795+
* `merge` - follow the merging algorithm outlined above
796+
* `append` - append elements that do not exist in it to the existing head, but don't remove any elements
797+
798+
#### Controlling Merge Behavior Per Element
799+
800+
You may also control merging behavior of individual elements with the following attributes:
801+
802+
* If you place `hx-head="re-eval"` on a head element, it will be re-added (removed and appended) to the head tag on every
803+
request, even if it already exists. This can be useful to execute a script on every htmx request, for example.
804+
* If you place `hx-preserve="true"` on an element, it will never be removed from the head, regardless
805+
806+
#### Configuring Boost & Non-Boost `head` behavior
807+
808+
You can configure the default head behavior by setting the `boost` and `other` attributes in the `htmx.config.head` object,
809+
using the following values:
810+
811+
* `merge` - merge the new head tag elements into the existing element
812+
* `append` - append the new head tag elements to the existing head tag
813+
* `none` - ignore any new head tag elements
814+
815+
By default, `htmx.config.head.boost` is `merge` and will apply to boosted links and forms.
816+
817+
`htmx.config.head.other` will apply to non-boosted requests, and defaults to `none` (that is, new head information) will
818+
be ignored.
819+
820+
You can also configure the head behavior using the [`hx-swap`](/attributes/hx-swap) attribute's `head` option.
821+
782822
### Progressive Enhancement {#progressive_enhancement}
783823

784824
A feature of `hx-boost` is that it degrades gracefully if javascript is not enabled: the links and forms continue

0 commit comments

Comments
 (0)