htmx

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

HTMX Skill

HTMX 使用指南

Provides guidance for building dynamic, interactive web applications using htmx - a library that enables modern browser features directly from HTML attributes.
本文提供使用htmx构建动态交互式Web应用的指南——htmx是一个可直接通过HTML属性启用现代浏览器功能的库。

Overview

概述

htmx extends HTML with attributes that allow any element to issue HTTP requests and update page content without writing JavaScript. It brings the power of AJAX, CSS Transitions, WebSockets, and Server-Sent Events directly into HTML markup.
Core Philosophy: Server returns HTML (not JSON), keeping you in the hypermedia/HATEOAS model. Any element can issue requests, not just anchors and forms. Any event can trigger requests, not just clicks and submissions.
htmx 通过扩展HTML属性,允许任何元素在无需编写JavaScript的情况下发送HTTP请求并更新页面内容。它将AJAX、CSS过渡、WebSockets和服务器发送事件的能力直接融入HTML标记中。
核心理念:服务器返回HTML(而非JSON),让你始终处于超媒体/HATEOAS模型中。任何元素都可以发送请求,而不仅仅是锚点和表单。任何事件都可以触发请求,而不仅仅是点击和提交操作。

Core Concepts

核心概念

AJAX Attributes

AJAX属性

Use these attributes to issue HTTP requests:
  • hx-get="/url"
    - Issues GET request
  • hx-post="/url"
    - Issues POST request
  • hx-put="/url"
    - Issues PUT request
  • hx-patch="/url"
    - Issues PATCH request
  • hx-delete="/url"
    - Issues DELETE request
html
<button hx-post="/clicked" hx-target="#result">
    Click Me!
</button>
<div id="result"></div>
使用以下属性发送HTTP请求:
  • hx-get="/url"
    - 发送GET请求
  • hx-post="/url"
    - 发送POST请求
  • hx-put="/url"
    - 发送PUT请求
  • hx-patch="/url"
    - 发送PATCH请求
  • hx-delete="/url"
    - 发送DELETE请求
html
<button hx-post="/clicked" hx-target="#result">
    Click Me!
</button>
<div id="result"></div>

Triggering Requests

触发请求

Control when requests fire with
hx-trigger
:
Default triggers:
input/textarea/select
use
change
,
form
uses
submit
, everything else uses
click
Custom triggers:
html
<!-- Trigger on mouseenter -->
<div hx-get="/data" hx-trigger="mouseenter">Hover me</div>

<!-- Trigger on keyup with delay -->
<input hx-get="/search" hx-trigger="keyup changed delay:500ms">

<!-- Multiple triggers -->
<div hx-get="/data" hx-trigger="mouseenter, focus">
Modifiers:
  • once
    - Only trigger once
  • changed
    - Only if value changed
  • delay:500ms
    - Wait before issuing request
  • throttle:1s
    - Rate limit requests
  • from:<selector>
    - Listen on different element
Filters:
html
<!-- Only trigger if Ctrl key pressed -->
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">Ctrl+Click</div>
Special events:
  • load
    - Fires when element loads
  • revealed
    - Fires when scrolled into viewport
  • every 2s
    - Poll every 2 seconds
使用
hx-trigger
控制请求何时触发:
默认触发条件
input/textarea/select
使用
change
事件,
form
使用
submit
事件,其他所有元素使用
click
事件
自定义触发条件:
html
<!-- 鼠标悬停时触发 -->
<div hx-get="/data" hx-trigger="mouseenter">Hover me</div>

<!-- 按键抬起并延迟触发 -->
<input hx-get="/search" hx-trigger="keyup changed delay:500ms">

<!-- 多个触发条件 -->
<div hx-get="/data" hx-trigger="mouseenter, focus">
修饰符:
  • once
    - 仅触发一次
  • changed
    - 仅当值发生变化时触发
  • delay:500ms
    - 延迟后发送请求
  • throttle:1s
    - 限制请求频率
  • from:<selector>
    - 监听不同元素的事件
过滤器:
html
<!-- 仅当按下Ctrl键时触发 -->
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">Ctrl+Click</div>
特殊事件:
  • load
    - 元素加载时触发
  • revealed
    - 元素滚动到视口中时触发
  • every 2s
    - 每2秒轮询一次

Targeting and Swapping

目标与内容交换

Control where and how content is inserted:
Target selection with
hx-target
:
html
<button hx-get="/data" hx-target="#result">Load</button>
<div id="result"></div>
Extended selectors:
  • this
    - The element itself
  • closest <selector>
    - Nearest ancestor matching selector
  • next <selector>
    - Next sibling matching selector
  • previous <selector>
    - Previous sibling matching selector
  • find <selector>
    - First child descendant
Swap strategies with
hx-swap
:
  • innerHTML
    (default) - Replace inner content
  • outerHTML
    - Replace entire element
  • afterbegin
    - Prepend inside target
  • beforebegin
    - Insert before target
  • beforeend
    - Append inside target
  • afterend
    - Insert after target
  • delete
    - Delete target regardless of response
  • none
    - Don't swap content
Swap modifiers:
html
<button hx-get="/data" hx-swap="innerHTML swap:100ms settle:200ms">
控制内容插入的位置和方式:
使用
hx-target
选择目标
:
html
<button hx-get="/data" hx-target="#result">Load</button>
<div id="result"></div>
扩展选择器:
  • this
    - 元素本身
  • closest <selector>
    - 匹配选择器的最近祖先元素
  • next <selector>
    - 匹配选择器的下一个兄弟元素
  • previous <selector>
    - 匹配选择器的上一个兄弟元素
  • find <selector>
    - 第一个匹配的子后代元素
使用
hx-swap
设置交换策略
:
  • innerHTML
    (默认)- 替换目标内部内容
  • outerHTML
    - 替换整个目标元素
  • afterbegin
    - 在目标内部前置内容
  • beforebegin
    - 在目标之前插入内容
  • beforeend
    - 在目标内部追加内容
  • afterend
    - 在目标之后插入内容
  • delete
    - 无论响应如何,删除目标元素
  • none
    - 不交换内容
交换修饰符:
html
<button hx-get="/data" hx-swap="innerHTML swap:100ms settle:200ms">

Request Indicators

请求指示器

Show loading state during requests:
html
<button hx-get="/slow">
    Click Me!
    <img class="htmx-indicator" src="/spinner.gif">
</button>
The
htmx-indicator
class has
opacity:0
by default. When request starts,
htmx-request
class is added to the element, which makes indicators visible.
Specify custom indicator target:
html
<button hx-get="/data" hx-indicator="#loading">Load</button>
<div id="loading" class="htmx-indicator">Loading...</div>
在请求期间显示加载状态:
html
<button hx-get="/slow">
    Click Me!
    <img class="htmx-indicator" src="/spinner.gif">
</button>
htmx-indicator
类默认
opacity:0
。当请求开始时,元素会被添加
htmx-request
类,使指示器可见。
指定自定义指示器目标:
html
<button hx-get="/data" hx-indicator="#loading">Load</button>
<div id="loading" class="htmx-indicator">Loading...</div>

Common Patterns

常见模式

Active Search

实时搜索

html
<input type="text" name="q"
    hx-get="/search"
    hx-trigger="keyup changed delay:500ms"
    hx-target="#search-results"
    placeholder="Search...">
<div id="search-results"></div>
html
<input type="text" name="q"
    hx-get="/search"
    hx-trigger="keyup changed delay:500ms"
    hx-target="#search-results"
    placeholder="Search...">
<div id="search-results"></div>

Infinite Scroll

无限滚动

html
<div hx-get="/more-items" 
     hx-trigger="revealed"
     hx-swap="afterend">
    Load More...
</div>
html
<div hx-get="/more-items" 
     hx-trigger="revealed"
     hx-swap="afterend">
    Load More...
</div>

Click to Edit

点击编辑

html
<div hx-get="/edit/123" hx-target="this" hx-swap="outerHTML">
    <label>Name:</label> John Doe
</div>
html
<div hx-get="/edit/123" hx-target="this" hx-swap="outerHTML">
    <label>Name:</label> John Doe
</div>

Delete with Confirmation

确认删除

html
<button hx-delete="/item/123"
        hx-confirm="Are you sure?"
        hx-target="closest tr"
        hx-swap="outerHTML swap:1s">
    Delete
</button>
html
<button hx-delete="/item/123"
        hx-confirm="Are you sure?"
        hx-target="closest tr"
        hx-swap="outerHTML swap:1s">
    Delete
</button>

Out-of-Band Swaps

带外交换

Update multiple parts of the page from one response:
html
<!-- Response HTML -->
<div id="main-content">Main update</div>
<div id="notification" hx-swap-oob="true">
    New notification!
</div>
The element with
hx-swap-oob="true"
swaps into its matching ID anywhere on the page.
通过一次响应更新页面的多个部分:
html
<!-- 响应HTML -->
<div id="main-content">Main update</div>
<div id="notification" hx-swap-oob="true">
    New notification!
</div>
带有
hx-swap-oob="true"
的元素会替换页面中ID匹配的元素。

Form Handling

表单处理

Basic Form Submission

基本表单提交

html
<form hx-post="/submit" hx-target="#result">
    <input name="email" type="email">
    <button type="submit">Submit</button>
</form>
html
<form hx-post="/submit" hx-target="#result">
    <input name="email" type="email">
    <button type="submit">Submit</button>
</form>

Including Additional Values

包含额外值

html
<!-- Include other elements -->
<button hx-post="/save" 
        hx-include="[name='email']">
    Save
</button>

<!-- Add extra values -->
<button hx-post="/save" 
        hx-vals='{"priority": "high"}'>
    Save
</button>
html
<!-- 包含其他元素的值 -->
<button hx-post="/save" 
        hx-include="[name='email']">
    Save
</button>

<!-- 添加额外值 -->
<button hx-post="/save" 
        hx-vals='{"priority": "high"}'>
    Save
</button>

File Upload

文件上传

html
<form hx-post="/upload" 
      hx-encoding="multipart/form-data"
      hx-target="#result">
    <input type="file" name="file">
    <button type="submit">Upload</button>
</form>
Listen for upload progress:
javascript
htmx.on('htmx:xhr:progress', function(evt) {
    htmx.find('#progress').value = evt.detail.loaded/evt.detail.total * 100;
});
html
<form hx-post="/upload" 
      hx-encoding="multipart/form-data"
      hx-target="#result">
    <input type="file" name="file">
    <button type="submit">Upload</button>
</form>
监听上传进度:
javascript
htmx.on('htmx:xhr:progress', function(evt) {
    htmx.find('#progress').value = evt.detail.loaded/evt.detail.total * 100;
});

Request Synchronization

请求同步

Coordinate requests between elements with
hx-sync
:
html
<form hx-post="/store">
    <input name="title" 
           hx-post="/validate"
           hx-trigger="change"
           hx-sync="closest form:abort">
    <button type="submit">Submit</button>
</form>
Strategies:
  • drop
    - Drop this request if target is in flight
  • abort
    - Abort target request if this triggers
  • replace
    - Abort target and issue this request
  • queue
    - Queue this request after target
使用
hx-sync
协调元素之间的请求:
html
<form hx-post="/store">
    <input name="title" 
           hx-post="/validate"
           hx-trigger="change"
           hx-sync="closest form:abort">
    <button type="submit">Submit</button>
</form>
策略:
  • drop
    - 如果目标请求正在进行,丢弃当前请求
  • abort
    - 如果当前请求触发,终止目标请求
  • replace
    - 终止目标请求并发送当前请求
  • queue
    - 在目标请求完成后排队发送当前请求

Boosting

增强功能

Progressive enhancement for regular links and forms:
html
<div hx-boost="true">
    <a href="/page1">Page 1</a>
    <a href="/page2">Page 2</a>
</div>
Links and forms become AJAX requests that target the body. Works without JavaScript enabled (graceful degradation).
为常规链接和表单提供渐进式增强:
html
<div hx-boost="true">
    <a href="/page1">Page 1</a>
    <a href="/page2">Page 2</a>
</div>
链接和表单会变成以body为目标的AJAX请求。在禁用JavaScript时仍可正常工作(优雅降级)。

History Support

历史记录支持

Add URLs to browser history:
html
<a hx-get="/blog" hx-push-url="true">Blog</a>
When user clicks back button, htmx restores the previous state. For history to work, URLs must return complete pages when visited directly.
Disable history caching for sensitive data:
html
<div hx-history="false">Sensitive content</div>
将URL添加到浏览器历史记录:
html
<a hx-get="/blog" hx-push-url="true">Blog</a>
当用户点击返回按钮时,htmx会恢复之前的状态。要使历史记录功能正常工作,直接访问URL时必须返回完整页面。
禁用敏感数据的历史记录缓存
html
<div hx-history="false">Sensitive content</div>

Headers

请求头

Request Headers

请求头

htmx automatically sends:
  • HX-Request: true
    - Identifies htmx requests
  • HX-Trigger
    - ID of triggering element
  • HX-Target
    - ID of target element
  • HX-Current-URL
    - Current page URL
  • HX-Prompt
    - User response to prompt
Use these to return partial HTML vs full pages:
python
if request.headers.get('HX-Request'):
    return render_template('partial.html')
return render_template('full_page.html')
htmx会自动发送以下请求头:
  • HX-Request: true
    - 标识htmx请求
  • HX-Trigger
    - 触发元素的ID
  • HX-Target
    - 目标元素的ID
  • HX-Current-URL
    - 当前页面URL
  • HX-Prompt
    - 用户对提示的响应
可以使用这些请求头来返回部分HTML还是完整页面:
python
if request.headers.get('HX-Request'):
    return render_template('partial.html')
return render_template('full_page.html')

Response Headers

响应头

Control client behavior from server:
  • HX-Trigger
    - Trigger client-side events
  • HX-Redirect
    - Client-side redirect (full page)
  • HX-Location
    - Client-side redirect (AJAX)
  • HX-Refresh
    - Force page refresh
  • HX-Retarget
    - Change target element
  • HX-Reswap
    - Change swap strategy
python
response.headers['HX-Trigger'] = 'itemUpdated'
response.headers['HX-Trigger'] = '{"showMessage": "Saved!"}'
通过服务器控制客户端行为:
  • HX-Trigger
    - 触发客户端事件
  • HX-Redirect
    - 客户端重定向(整页)
  • HX-Location
    - 客户端重定向(AJAX)
  • HX-Refresh
    - 强制页面刷新
  • HX-Retarget
    - 更改目标元素
  • HX-Reswap
    - 更改交换策略
python
response.headers['HX-Trigger'] = 'itemUpdated'
response.headers['HX-Trigger'] = '{"showMessage": "Saved!"}'

Validation

验证

htmx integrates with HTML5 validation:
html
<form hx-post="/submit">
    <input name="email" type="email" required>
    <button type="submit">Submit</button>
</form>
Set
htmx.config.reportValidityOfForms = true
to show validation messages.
Custom validation:
javascript
htmx.on('htmx:validation:validate', function(evt) {
    if (evt.target.value === 'forbidden') {
        evt.target.setCustomValidity('This value is forbidden');
        evt.detail.valid = false;
    }
});
htmx与HTML5验证集成:
html
<form hx-post="/submit">
    <input name="email" type="email" required>
    <button type="submit">Submit</button>
</form>
设置
htmx.config.reportValidityOfForms = true
以显示验证消息。
自定义验证:
javascript
htmx.on('htmx:validation:validate', function(evt) {
    if (evt.target.value === 'forbidden') {
        evt.target.setCustomValidity('This value is forbidden');
        evt.detail.valid = false;
    }
});

Events and Scripting

事件与脚本

Event Handling

事件处理

Use
hx-on
for inline event handlers:
html
<button hx-get="/data" 
        hx-on::before-request="this.classList.add('loading')"
        hx-on::after-request="this.classList.remove('loading')">
    Load
</button>
使用
hx-on
设置内联事件处理程序:
html
<button hx-get="/data" 
        hx-on::before-request="this.classList.add('loading')"
        hx-on::after-request="this.classList.remove('loading')">
    Load
</button>

JavaScript API

JavaScript API

javascript
// Trigger requests programmatically
htmx.ajax('GET', '/data', '#target');

// Listen to events
htmx.on('htmx:afterSwap', function(evt) {
    console.log('Content swapped');
});

// Process new content
htmx.process(document.body);

// Trigger events
htmx.trigger('#element', 'myEvent', {detail: {foo: 'bar'}});
javascript
// 以编程方式触发请求
htmx.ajax('GET', '/data', '#target');

// 监听事件
htmx.on('htmx:afterSwap', function(evt) {
    console.log('Content swapped');
});

// 处理新内容
htmx.process(document.body);

// 触发事件
htmx.trigger('#element', 'myEvent', {detail: {foo: 'bar'}});

CSS Transitions

CSS过渡

Keep element IDs stable across swaps for automatic transitions:
html
<!-- Before -->
<div id="content">Old content</div>

<!-- After (same ID) -->
<div id="content" class="highlight">New content</div>
css
.highlight {
    background-color: yellow;
    transition: background-color 1s ease-in;
}
htmx preserves the DOM element and transitions the class change.
在内容交换过程中保持元素ID稳定以实现自动过渡:
html
<!-- 交换前 -->
<div id="content">Old content</div>

<!-- 交换后(相同ID) -->
<div id="content" class="highlight">New content</div>
css
.highlight {
    background-color: yellow;
    transition: background-color 1s ease-in;
}
htmx会保留DOM元素并过渡类的变化。

Configuration

配置

Configure globally or via meta tag:
html
<meta name="htmx-config" content='{
    "defaultSwapStyle": "outerHTML",
    "defaultSwapDelay": 100,
    "defaultSettleDelay": 200,
    "historyCacheSize": 20
}'>
Or in JavaScript:
javascript
htmx.config.defaultSwapStyle = 'outerHTML';
htmx.config.timeout = 5000; // 5 second timeout
全局配置或通过meta标签配置:
html
<meta name="htmx-config" content='{
    "defaultSwapStyle": "outerHTML",
    "defaultSwapDelay": 100,
    "defaultSettleDelay": 200,
    "historyCacheSize": 20
}'>
或在JavaScript中配置:
javascript
htmx.config.defaultSwapStyle = 'outerHTML';
htmx.config.timeout = 5000; // 5秒超时

Installation

安装

CDN (Recommended for quick start)

CDN(推荐快速入门使用)

html
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>
html
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>

npm

npm

bash
npm install htmx.org@2.0.8
Then import:
javascript
import 'htmx.org';
bash
npm install htmx.org@2.0.8
然后导入:
javascript
import 'htmx.org';

Download

下载

Download from jsDelivr and include locally:
html
<script src="/js/htmx.min.js"></script>
从jsDelivr下载并本地引入:
html
<script src="/js/htmx.min.js"></script>

Best Practices

最佳实践

  1. Keep IDs stable - Use consistent IDs across requests for CSS transitions
  2. Return appropriate content - Return partials for htmx requests, full pages for direct access
  3. Use semantic HTML - htmx enhances HTML, so start with good markup
  4. Progressive enhancement - Use
    hx-boost
    so features work without JavaScript
  5. Handle errors - Listen to
    htmx:responseError
    and
    htmx:sendError
    events
  6. Validate inputs - Enable
    htmx.config.reportValidityOfForms = true
  7. Test without JavaScript - Ensure core functionality works when JS is disabled
  1. 保持ID稳定 - 在请求之间使用一致的ID以实现CSS过渡
  2. 返回合适的内容 - 为htmx请求返回部分HTML,为直接访问返回完整页面
  3. 使用语义化HTML - htmx是增强HTML的工具,因此从良好的标记开始
  4. 渐进式增强 - 使用
    hx-boost
    确保在禁用JavaScript时功能仍可使用
  5. 处理错误 - 监听
    htmx:responseError
    htmx:sendError
    事件
  6. 验证输入 - 启用
    htmx.config.reportValidityOfForms = true
  7. 在无JavaScript环境下测试 - 确保核心功能在禁用JS时仍能正常工作

Debugging

调试

Enable logging:
javascript
htmx.logAll();
Or set custom logger:
javascript
htmx.logger = function(elt, event, data) {
    if(console) {
        console.log(event, elt, data);
    }
}
Use browser DevTools to inspect:
  • Network tab for request/response details
  • HX-*
    headers in request/response
  • Event listeners on elements
  • htmx-*
    classes during swap lifecycle
启用日志:
javascript
htmx.logAll();
或设置自定义日志记录器:
javascript
htmx.logger = function(elt, event, data) {
    if(console) {
        console.log(event, elt, data);
    }
}
使用浏览器开发者工具检查:
  • 网络标签页查看请求/响应详情
  • 请求/响应中的
    HX-*
  • 元素上的事件监听器
  • 交换生命周期中的
    htmx-*

Additional Resources

额外资源

For comprehensive details:
  • references/attributes.md
    - Complete attribute reference
  • references/events.md
    - All htmx events and lifecycle
  • references/examples.md
    - Advanced patterns and real-world examples
  • references/server-side.md
    - Server-side implementation patterns
如需详细信息:
  • references/attributes.md
    - 完整的属性参考
  • references/events.md
    - 所有htmx事件和生命周期
  • references/examples.md
    - 高级模式和真实示例
  • references/server-side.md
    - 服务器端实现模式