Preserve child head content in HTMX embeds
This commit is contained in:
parent
c8e50cb629
commit
cda04f48ed
1 changed files with 23 additions and 9 deletions
32
index.js
32
index.js
|
|
@ -416,15 +416,32 @@ function decoratePage(html, { headInjection, source } = {}) {
|
|||
return $.html();
|
||||
}
|
||||
|
||||
// HTMX partial: stamp provenance on the swapped-in elements (an oob swap loses
|
||||
// its own attrs, so we descend into its children) and append an out-of-band
|
||||
// <head> so head injection still reaches the page.
|
||||
// Carry rendered head content through HTMX with a neutral OOB wrapper. A
|
||||
// literal <head> in an HTMX response is discarded by the browser's fragment
|
||||
// parser, and HTMX does not discover a top-level <style hx-swap-oob>. The div
|
||||
// survives long enough for HTMX to append its children to the host's real
|
||||
// #head; the browser then keeps the valid style/link/meta children there.
|
||||
function headOobSwaps(headHtml, headInjection) {
|
||||
const $ = cheerio.load(
|
||||
`<head>${headHtml || ""}${headInjection || ""}</head>`,
|
||||
);
|
||||
const contents = $("head").html();
|
||||
return contents ? `<div hx-swap-oob="beforeend:#head">${contents}</div>` : "";
|
||||
}
|
||||
|
||||
// HTMX partial: stamp provenance on the swapped-in body elements (an OOB swap
|
||||
// loses its own attrs, so we descend into its children), then carry the child
|
||||
// structure's rendered head and configured head injection into the host page.
|
||||
function decorateFragment(html, { headInjection, source } = {}) {
|
||||
const $ = cheerio.load(html, null, false);
|
||||
// Parse as a document so browser-valid top-level styles and links are
|
||||
// collected into head. `cheerio.load(..., null, false)` leaves those nodes as
|
||||
// fragment siblings, which makes them get swapped into body and not apply.
|
||||
const $ = cheerio.load(html);
|
||||
const body = $("body");
|
||||
const attrs = blissAttrs(source);
|
||||
if (attrs) {
|
||||
const targets = [];
|
||||
for (const child of $.root().children()) {
|
||||
for (const child of body.children()) {
|
||||
if (child.attribs && child.attribs["hx-swap-oob"]) {
|
||||
for (const grandchild of child.children) {
|
||||
if (grandchild.attribs) targets.push(grandchild);
|
||||
|
|
@ -435,10 +452,7 @@ function decorateFragment(html, { headInjection, source } = {}) {
|
|||
}
|
||||
for (const target of targets) Object.assign(target.attribs, attrs);
|
||||
}
|
||||
return (
|
||||
$.html() +
|
||||
`<head id="head" hx-oob-swap="beforeend">${headInjection || ""}</head>`
|
||||
);
|
||||
return body.html() + headOobSwaps($("head").html(), headInjection);
|
||||
}
|
||||
|
||||
// `res.render()` commonly returns a complete document even when it is serving
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue