htmx
htmx is a library that allows you to access modern browser features directly from HTML, rather than using javascript.
Ajax
Triggers
请求类型
Attribute | Description |
---|---|
hx-get | Issues a GET request to the given URL |
hx-post | Issues a POST request to the given URL |
hx-put | Issues a PUT request to the given URL |
hx-patch | Issues a PATCH request to the given URL |
hx-delete | Issues a DELETE request to the given URL |
触发方式 hx-trigger
默认的触发方式
input
,textarea
&select
are triggered on thechange
eventform
is triggered on thesubmit
event- everything else is triggered by the
click
event
使用 hx-trigger 来指定触发方式
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[Here Mouse, Mouse!]
</div>
触发行为的附加状态
Trigger Modifiers
使用一些附加状态来进一步描述触发行为
-
once
- 仅首次触发时会发送请求 -
changed
- 仅在元素的值变化时发送请求 -
delay:<time interval>
- 等X秒后才发送请求。如果再次触发了,countdown将会重置 -
throttle:<time interval>
- 等X秒后才发送请求。如果在 countdown 未完成前再次触发,这次出发无效。所以会保证再一次完成的 countdown 后发送请求 -
from:<CSS Selector>
- 监听另一个元素的触发时间- This can be used for things like keyboard shortcuts. Note that this CSS selector is not re-evaluated if the page changes.
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
[Here Mouse, Mouse!]
</div>
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Search..."
>
<div id="search-results"></div>
可以指定多种触发方式,separated by commas.
过滤触发条件
Trigger Filters
使用方括号包括一段 javascript expression 来过滤触发条件
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Control Click Me
</div>
特殊的触发事件
一些特殊的触发事件:
-
load
- 当首次加载元素时触发 -
revealed
- 当元素首次出现在视区中时触发(first scrolls into the viewport)- revealed 指元素的一部分进入视区,intersect 指完全进入视区
-
intersect
- 当元素首次与视区相交时触发(first intersects the viewport)。有两种选项:root:<selector>
- a CSS selector of the root element for intersectionthreshold:<float>
- a floating point number between 0.0 and 1.0, indicating what amount of intersection to fire the event on
-
every Xs
:每个 x 秒轮询触发- 若响应返回
286
,则会中止轮询
- 若响应返回
加载指示 hx-indicator
Request Indicators
让 htmx 发送请求时,它会给元素添加一个 htmx-request
class (either the requesting element or another element, if specified),其任何带有 htmx-request
class 的子元素(通常就是一段加载动画)的透明度会被拉到一,显现出来
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
如果想自定义 htmx-request
class 的元素 (而非只能是子元素),可以使用 hx-indicator 并提供一个 CSS 选择器来指定:
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click Me!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif"/>
</div>
如此这般,尽管 img 标签并不是 button 的子元素,但是被 hx-indicator
指定后,也是相同的效果
改变响应目标 hx-target
使用 hx-target 并提供 CSS 选择器来改变 response 影响的目标(默认是发出请求的标签本身)
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search..."
>
<div id="search-results"></div>
CSS 选择器的扩展语法
Extended CSS Selectors
this
- 指向标签本身closest <CSS selector>
- 指向靠自己最近的、命中 CSS 选择器的祖先元素- e.g.
closest tr
will target the closest table row to the element
- e.g.
next <CSS selector>
- DOM 中下一个、命中 CSS 选择器的元素previous <CSS selector>
- DOM 中上一个、命中 CSS 选择器的元素find <CSS selector>
- 第一个命中 CSS 选择器的子辈元素- e.g
find tr
would target the first child descendant row to the element
- e.g
In addition, a CSS selector may be wrapped in <
and />
characters, mimicking the query literal syntax of hyperscript.
交换元素 hx-swap
默认为 innerHTML
Name | Description |
---|---|
innerHTML | the default, 把内容塞进目标元素里面 |
outerHTML | 目标元素直接被返回的内容替代 |
afterbegin | 在目标内的第一个子元素之前预置内容 |
beforebegin | 在目标元素的父元素中,将内容置于目标之前 |
beforeend | 在目标内的最后一个子元素之后预置内容 |
afterend | 在目标元素的父元素中,将内容置于目标之后 |
delete | 不管返回如何,删除目标元素 |
none | does not append content from response (Out of Band Swaps and Response Headers will still be processed) |
一些额外的配置项
Option | Description |
---|---|
transition | true or false , 是否使用 view transition API |
swap | (e.g. 100ms ) 在老内容被清除和新内容被插入之间,停留的时长 |
settle | The settle delay to use (e.g. 100ms ) 在新内容被插入和完成(when it is settled)之间,停留的时长 |
ignoreTitle | If set to true , 新内容中的 title 不会影响到 document title |
scroll | top or bottom , will scroll the target element to its top or bottom |
show | top or bottom , will scroll the target element’s top or bottom into view |
请求间同步 hx-sync
看这个例子:
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change">
<button type="submit">Submit</button>
</form>
没有 hx-sync
的 话, 在 input 中输入内容后立即提交 form,将会同时触发两个 requests to /validate
and /store
添加 hx-sync="closest form:abort"
后,input 会在 closest form 发出请求时,将自己的请求取消掉
<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Submit</button>
</form>
sync 策略
-
drop
- (the default) 如果当前请求正在发送,则忽略/舍去之- drop (ignore) this request if an existing request is in flight
-
abort
- 如果当前请求正在发送,则忽略/舍去之;或者,若另一个请求发生了,忽略/社区当前的请求- drop (ignore) this request if an existing request is in flight, and, if that is not the case, abort this request if another request occurs while it is still in flight
-
replace
- abort the current request, if any, and replace it with this request -
queue
- place this request in the request queue associated with the given element-
The
queue
modifier can take an additional argument indicating exactly how to queue:-
queue first
- queue the first request to show up while a request is in flight -
queue last
- queue the last request to show up while a request is in flight -
queue all
- queue all requests that show up while a request is in flight
-
-
选择响应 hx-select
hx-select
允许你从返 回的响应中手动选择你要的内容去 swap,只要提供一个 CSS 选择器即可
Here is an example that selects a subset of the response content:
<div>
<button hx-get="/info" hx-select="#info-details" hx-swap="outerHTML">
Get Info!
</button>
</div>
So this button will issue a GET
to /info
and then select the element with the id info-detail
, which will replace the entire button in the DOM.
hx-select
is inherited and can be placed on a parent element
自定义请求参数
默认情况下,元素发出请求时,会带上自己的参数(if it has one),比如 form 元素就会带上自己所有的 input 标签的 name
值
If you wish to filter out some parameters you can use the hx-params attribute.
Finally, if you want to programmatically modify the parameters, you can use the htmx:configRequest event.
额外添加其他元素作为请求参数 hx-include
hx-include
attribute 允许你在请求中添加额外的元素值
<div>
<button hx-post="/register" hx-include="[name='email']">
Register!
</button>
Enter email: <input name="email" type="email"/>
</div>
The value of this attribute can be:
- CSS 选择器,选择你想要包含的元素
this
,包含了当前元素及其子辈closest <CSS selector>
使用 closest 语法的 CSS 选择器find <CSS selector>
获取当前元素中首个匹配选择器的子辈元素next <CSS selector>
当前元素下一个匹配选择器的元素previous <CSS selector>
当前元素上一个匹配选择器的元素
tips
hx-include
可被继承
过滤请求参数 hx-params
hx-params
attribute 可以用来指定请求参数
The possible values of this attribute are:
*
- (default) 包括所有参数none
- 啥都不传not <param-list>
- 不传 param-list 中的参数<param-list>
- 只传 param-list 中的参数
<div hx-get="/example" hx-params="*">Get Some HTML, Including Params</div>
This div will include all the parameters that a POST
would, but they will be URL encoded and included in the URL, as per usual with a GET
.
tips:
hx-params
is inherited and can be placed on a parent element
添加额外的参数 hx-vals, hx-vars
-
hx-vals
接受 json -
hx-vars
接受单值列表:a comma separated list ofname
:<expression>
<div hx-get="/example" hx-vals='{"myVal": "My Value"}'>Get Some HTML, Including A Value in the Request</div>
<div hx-get="/example" hx-vals='js:{myVal: calculateValue()}'>Get Some HTML, Including a Dynamic Value from Javascript in the Request</div>
<div hx-get="/example" hx-vars="myVar:computeMyVar()">Get Some HTML, Including A Dynamic Value in the Request</div>
When using evaluated code you can access the event
object. This example includes the value of the last typed key within the input.
<div hx-get="/example" hx-trigger="keyup" hx-vals='js:{lastKey: event.key}'>
<input type="text" />
</div>
确认请求 hx-confirm
Confirming Requests
使用 hx-confirm
attribute, which allows you to confirm an action using a simple javascript dialog:
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
Using events you can implement more sophisticated confirmation dialogs. The confirm example shows how to use sweetalert2 library for confirmation of htmx actions.
其他技巧
另一种轮询的方式:使用 load + delay,并且使用返回值替换自身,达成轮询的效果(适合用在进度条上)
<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML"
>
</div>
htmx 也提供了可编程的方法来取消请求,将 htmx:abort
事件发送给任意元素 to cancel any in-flight requests:
<button id="request-button" hx-post="/example">
Issue Request
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Request
</button>
关于 closest:获取匹配特定选择器且离当前元素最近的祖先元素(也可以是当前元素本身)
<article>
<div id="div-01">
Here is div-01
<div id="div-02">
Here is div-02
<div id="div-03">Here is div-03</div>
</div>
</div>
</article>
var el = document.getElementById("div-03");
var r1 = el.closest("#div-02");
// 返回 id 为 div-02 的那个元素
var r2 = el.closest("div div");
// 返回最近的拥有 div 祖先元素的 div 祖先元素,这里的话就是 div-03 元素本身
var r3 = el.closest("article > div");
// 返回最近的拥有父元素 article 的 div 祖先元素,这里的话就是 div-01
var r4 = el.closest(":not(div)");
// 返回最近的非 div 的祖先元素,这里的话就是最外层的 article
继承性
大部分 hx 属性都是可继承的,这能减少很多重复的代码量
Consider the following htmx:
<button hx-delete="/account" hx-confirm="Are you sure?">
Delete My Account
</button>
<button hx-put="/account" hx-confirm="Are you sure?">
Update My Account
</button>
直接把 hx-confirm 写在这两个 button 的父元素上
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
</div>
unset
有时候你并不需要继承。我们使用 hx-confirm="unset"
来取消继承关系:
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
<button hx-confirm="unset" hx-get="/">
Cancel
</button>
</div>
其他属性同理,赋上 "unset"
值即可。
hx-disinherit
hx-disinherit
attribute 可以指定哪些属性不被继承
- 当 hx-disinherit 在一个父元素上时
hx-disinherit="*"
- 父元素上的所有属性都不会被子辈元素继承hx-disinherit="hx-select hx-get hx-target"
- 指定哪些属性不会被继承
<div hx-boost="true" hx-select="#content" hx-target="#content" hx-disinherit="*">
<a href="/page1">Go To Page 1</a> <!-- boosted with the attribute settings above -->
<a href="/page2" hx-boost="unset">Go To Page 1</a> <!-- not boosted -->
<button hx-get="/test" hx-target="this"></button> <!-- hx-select is not inherited -->
</div>
<div hx-select="#content">
<div hx-boost="true" hx-target="#content" hx-disinherit="hx-select">
<!-- hx-target is automatically inherited from parent's value -->
<!-- hx-select is not inherited, because the direct parent does
disables inheritance, despite not specifying hx-select itself -->
<button hx-get="/test"></button>
</div>
</div>
hx-inherit
如果你想彻底禁用默认的继承方式,you can set the htmx.config.disableInheritance
configuration variable to true
然后,你就可以用 hx-inherit
attribute 来指定你想要继承的属性了
<div hx-target="#tab-container" hx-inherit="hx-target">
<a hx-boost="true" href="/tab1">Tab 1</a>
<a hx-boost="true" href="/tab2">Tab 2</a>
<a hx-boost="true" href="/tab3">Tab 3</a>
</div>
处理请求响应
默认情况下,htmx 期望请求的响应是一段 HTML,以便 swap
- 但有时候,仅仅是想触发一下请求,并不想处理返回的东西。这个时候,可以把响应码设置为
204 - No Content
,这样 htmx 就会忽略返回的内容了 - 如果响应码是服务器相关的错误(e.g. a 404 or a 501),htmx will trigger the
htmx:responseError
event, which you can handle - 如果是链接错误的话, the
htmx:sendError
event will be triggered.
自定义响应策略
Configuring Response Handling
修改 htmx.config.responseHandling
array 来自定义响应策略,This object is a collection of JavaScript objects defined like so:
responseHandling: [
{code:"204", swap: false}, // 204 - No Content by default does nothing, but is not an error
{code:"[23]..", swap: true}, // 200 & 300 responses are non-errors and are swapped
{code:"[45]..", swap: false, error:true}, // 400 & 500 responses are not swapped and are errors
{code:"...", swap: false} // catch all for any other response code
]
htmx 会按照 htmx.config.responseHandling
array 的顺序尝试命中策略
可以使用的策略选项:
code
- 响应码swap
- 是否将响应内容 swaperror
- 是否将此响应码视为错误ignoreTitle
- 是否忽略 响应中的 title tagsselect
- CSS 选择器,挑选响应中的哪些内容会被 swaptarget
- CSS 选择器,挑选会被响应替换的目标swapOverride
- An alternative swap mechanism for the response
Using the meta config mechanism for configuring responseHandling, we could add the following config:
<!--
* 204 No Content by default does nothing, but is not an error
* 2xx, 3xx and 422 responses are non-errors and are swapped
* 4xx & 5xx responses are not swapped and are errors
* all other responses are swapped using "..." as a catch-all
-->
<meta
name="htmx-config"
content='{
"responseHandling":[
{"code":"204", "swap": false},
{"code":"[23]..", "swap": true},
{"code":"422", "swap": true},
{"code":"[45]..", "swap": false, "error":true},
{"code":"...", "swap": true}
]
}'
/>
If you wanted to swap everything, regardless of HTTP response code, you could use this configuration:
<meta name="htmx-config" content='{code:".*", swap: true}, // all responses are swapped'>