htmx

交换

响应的 HTML 拿到手,往哪里放、怎么放——由 hx-targethx-swap 共同决定。「为什么只能替换整个页面?」 这一问,本章作答。

八种策略

hx-swap 的取值,本质是一组 DOM 插入位置加两个特殊指令:

语义
innerHTML(默认)替换目标元素的内部内容
outerHTML连同目标元素自身一起替换
beforebegin插到目标之前
afterbegin插到目标内部的开头
beforeend插到目标内部的末尾
afterend插到目标之后
delete删除目标元素(响应被忽略)
none什么都不换——只借请求触发副作用与事件
「替换整个页面」只是浏览器最早的默认值;在 htmx 的世界,替换粒度由你钦定

修饰符

策略之后可缀以分号分隔的修饰符:

hx-swap 修饰符语法
<div hx-swap="innerHTML swap:0.5s settle:1s scroll:top show:.app">…</div>

<!-- swap:0.5s   延迟 500ms 再换入(可让出场动画先跑完)
     settle:1s  换入后延迟 1s 再添加 htmx-settling 类
     scroll:top 换入后把目标滚动到顶部(可配滚动容器)
     show:.app  换入后让目标在 .app 滚动容器内可见
     transition:true  启用 CSS View Transitions -->

延迟换入的妙用:请求返回后,先给「旧内容」留一段体面的退场时间,再瞬间换入新内容,观感上毫无等待——这是 htmx 官方教程中著名的 swap: 技巧。

过渡动画

htmx 用三个 CSS 类把生命周期暴露给样式表,过渡信手拈来:

何时出现
.htmx-request请求进行中(挂在触发元素与 hx-indicator 指定的元素上)
.htmx-swapping旧内容即将移除时(配合 transition CSS 做退场)
.htmx-settling换入之后、结构稳定前的瞬间(做入场补间)
一段纸墨风的换入淡入 · theme.css
.htmx-swapping { opacity: 0; transition: opacity .25s ease-out; }
.htmx-settling { background: var(--cinnabar-wash); transition: background 1s ease; }
若旧内容的退场动画没跑完就被替换,多半是忘了 swap: 延迟——给动画让路,再换。

旁路交换(Out of Band Swaps)

一次请求,多处落点:响应里给「捎带更新」的片段标上 hx-swap-oob,主目标照常换,旁路各归各家:

一次删除,两处更新 · 服务端响应
<!-- 主内容:删除的行换成空 -->
<tr id="row-7"></tr>

<!-- 旁路:顺手刷新角标计数 -->
<span id="cart-count" hx-swap-oob="true">3 件</span>

<!-- 旁路还能自带策略 -->
<aside id="toast" hx-swap-oob="afterend">已删去第 7 行</aside>

发起端只需处理主目标即可;多个旁路时配合 hx-select-oob 精确点名。

交换实验室

下面的演示直接打本站服务器——每一枚时间戳卡片,都是一次真实 HTTP 往返生成的 HTML:

交换实验室 · 实时
这里还空着——点上面的按钮,让服务器生成 HTML 换进来。
下拉框经 hx-on:change 实时改写按钮的 hx-swap——换成 beforeend 再点「生成一枚时间戳」,新卡片便层层叠上;「追加」按钮则固定用 beforeend 只加在末尾。这就是只换一角刷新整页的区别。
静态快照 · 部分演示需动态服务器