跳到主要内容

htmx

https://htmx.org/docs

htmx is a library that allows you to access modern browser features directly from HTML, rather than using javascript.

Ajax

Triggers

请求类型

AttributeDescription
hx-getIssues a GET request to the given URL
hx-postIssues a POST request to the given URL
hx-putIssues a PUT request to the given URL
hx-patchIssues a PATCH request to the given URL
hx-deleteIssues a DELETE request to the given URL

触发方式 hx-trigger

默认的触发方式

  • input, textarea & select are triggered on the change event
  • form is triggered on the submit 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 intersection
    • threshold:<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
  • 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

In addition, a CSS selector may be wrapped in < and /> characters, mimicking the query literal syntax of hyperscript.

交换元素 hx-swap

https://htmx.org/attributes/hx-swap/

默认为 innerHTML

NameDescription
innerHTMLthe default, 把内容塞进目标元素里面
outerHTML目标元素直接被返回的内容替代
afterbegin在目标内的第一个子元素之前预置内容
beforebegin在目标元素的父元素中,将内容置于目标之前
beforeend在目标内的最后一个子元素之后预置内容
afterend在目标元素的父元素中,将内容置于目标之后
delete不管返回如何,删除目标元素
nonedoes not append content from response (Out of Band Swaps and Response Headers will still be processed)

一些额外的配置项

OptionDescription
transitiontrue or false, 是否使用 view transition API
swap(e.g. 100ms) 在老内容被清除和新内容被插入之间,停留的时长
settleThe settle delay to use (e.g. 100ms) 在新内容被插入和完成(when it is settled)之间,停留的时长
ignoreTitleIf set to true, 新内容中的 title 不会影响到 document title
scrolltop or bottom, will scroll the target element to its top or bottom
showtop or bottom, will scroll the target element’s top or bottom into view

请求间同步 hx-sync

https://htmx.org/attributes/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

https://htmx.org/attributes/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 of name:<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

  1. 但有时候,仅仅是想触发一下请求,并不想处理返回的东西。这个时候,可以把响应码设置为 204 - No Content,这样 htmx 就会忽略返回的内容了
  2. 如果响应码是服务器相关的错误(e.g. a 404 or a 501),htmx will trigger the htmx:responseError event, which you can handle
  3. 如果是链接错误的话, 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 - 是否将响应内容 swap
  • error - 是否将此响应码视为错误
  • ignoreTitle - 是否忽略 响应中的 title tags
  • select - CSS 选择器,挑选响应中的哪些内容会被 swap
  • target - 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'>