- 前端
【免费下载链接】htmx
htmx - high power tools for HTML
htmx 以声明式 HTML 属性(hx-*)闻名,但它同时提供了一套小而精的 JavaScript API,用于在需要编程控制的场景中发起 AJAX 请求、操作 DOM、管理事件与扩展。本指南以官方 API 文档 为主体,结合仓库源码 src/htmx.js 中的真实实现与 test/core/api.js 测试用例,完整讲解每个方法、属性的签名、参数、默认值与底层原理。读完本文,你将能够脱离属性声明,用纯 JavaScript 完成请求触发、内容交换、事件监听、值解析、类名控制与扩展注册,并理解htmx.config中每一项配置对运行时行为的实际影响。
API 的定位与适用场景
htmx 官方文档明确说明:虽然编程式调用并非库的专注点,htmx 仍然提供了一小组辅助方法,主要面向扩展开发(extension development)与事件管理(event management)。需要更完整脚本化支持的应用,可以配合 hyperscript 项目使用。
整个 API 定义在仓库的 src/htmx.js 中:一个htmx对象先以空引用声明全部入口(onLoad、process、on、off、trigger、ajax、find、findAll、closest、values、remove、addClass、removeClass、toggleClass、takeClass、swap、defineExtension、removeExtension、logAll、logNone、parseInterval等),随后在 L302-L322 将这些引用逐一绑定到内部实现函数。与此同时,htmx还暴露了两个内部性质的口子:htmx._(internalEval,见 src/htmx.js#L863-L867)与htmx.location,以及只读的htmx.version(当前仓库版本为2.0.11,见 src/htmx.js#L299)。
这些 API 的完整 TypeScript 类型声明可在 src/htmx.esm.d.ts 中查阅,包含HtmxAjaxHelperContext、HtmxSwapSpecification、HtmxExtension等辅助类型,是扩展开发者最可靠的签名参考。
运行时配置:htmx.config
htmx.config是一个保存 htmx 运行时全部配置的属性。官方文档特别指出,使用<meta>标签是设置这些属性的首选机制(见 docs.md 的配置章节),因为它可以避免脚本执行时序问题:
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}' />等效的编程式写法:
// 将历史缓存大小更新为 30 htmx.config.historyCacheSize = 30;从源码看,所有配置项的默认值集中定义在 src/htmx.js#L77-L289,官方文档与源码逐项对应,完整清单如下:
| 配置属性 | 默认值 | 说明 |
|---|---|---|
attributesToSettle | ["class", "style", "width", "height"] | 在 settling(沉降)阶段需要合并的属性列表 |
refreshOnHistoryMiss | false | 为true时,历史记录 miss 将触发整页刷新而非 AJAX 请求 |
defaultSettleDelay | 20 | 内容交换完成后到属性沉降之间的默认延迟(毫秒) |
defaultSwapDelay | 0 | 收到服务器响应后到执行交换之间的默认延迟(毫秒) |
defaultSwapStyle | 'innerHTML' | 未写hx-swap时的默认交换方式 |
historyCacheSize | 10 | 历史支持在sessionStorage中保留的页面数 |
historyEnabled | true | 是否启用历史功能 |
includeIndicatorStyles | true | 为true时注入少量 CSS,使指示器在缺少htmx-indicator类时不可见 |
indicatorClass | 'htmx-indicator' | 请求进行中放置到指示器上的类 |
requestClass | 'htmx-request' | 请求进行中放置到触发元素上的类 |
addedClass | 'htmx-added' | 临时放置到 htmx 新增 DOM 元素上的类 |
settlingClass | 'htmx-settling' | settling 阶段放置到目标元素上的类 |
swappingClass | 'htmx-swapping' | swapping 阶段放置到目标元素上的类 |
allowEval | true | 是否允许 htmx 使用类 eval 功能(启用hx-vars、触发条件与 script 标签求值);可设为false以兼容 CSP |
allowScriptTags | true | 是否允许新内容中的 script 标签被求值 |
inlineScriptNonce | '' | 添加到内联脚本上的 nonce 值 |
inlineStyleNonce | '' | 添加到内联样式上的 nonce 值 |
withCredentials | false | 是否允许携带凭证(cookie、授权头、TLS 客户端证书)的跨站 Access-Control 请求 |
timeout | 0 | 请求超时毫秒数,超过后自动终止 |
wsReconnectDelay | 'full-jitter' | getWebSocketReconnectDelay的默认实现,用于Abnormal Closure、Service Restart、Try Again Later等异常断开后的重连 |
wsBinaryType | 'blob' | WebSocket 连接接收的二进制数据类型 |
disableSelector | "[hx-disable], [data-hx-disable]" | 带有该属性(或其父级带有)的元素不会被 htmx 处理 |
disableInheritance | false | 为true时完全禁用属性继承,可配合hx-inherit显式指定继承 |
scrollBehavior | 'instant' | hx-swap的show修饰符使用的滚动行为,取值instant/smooth/auto |
defaultFocusScroll | false | 是否将聚焦元素滚动到视口内,可用focus-scroll交换修饰符覆盖 |
getCacheBusterParam | false | 为true时在GET请求上追加org.htmx.cache-buster=targetElementId参数 |
globalViewTransitions | false | 为true时,交换新内容使用 View Transition API |
methodsThatUseUrlParams | ["get", "delete"] | 这些方法将参数编码进 URL 而非请求体 |
selfRequestsOnly | true | 是否只允许向当前文档同域发起 AJAX 请求 |
ignoreTitle | false | 为true时,新内容中的title标签不再更新文档标题 |
scrollIntoViewOnBoost | true | boosted 元素的目标是否滚动进视口;未写hx-target时目标默认为body,会导致页面滚回顶部 |
triggerSpecsCache | null | 存储已求值触发规格的缓存对象,用内存换解析性能;可传普通对象或基于 Proxy 的自定义实现 |
responseHandling | 见下文 | 响应状态码的默认处理方式(交换或报错) |
allowNestedOobSwaps | true | 是否处理嵌套在主响应元素内的 OOB 交换,见hx-swap-oob的嵌套 OOB |
historyRestoreAsHxRequest | true | 历史缓存 miss 的整页刷新请求是否作为HX-Request返回响应头;若依赖HX-Request头选择性返回局部响应,应始终禁用 |
reportValidityOfForms | false | 是否向用户报告输入校验错误并将焦点移到第一个校验失败的表单控件(与浏览器默认提交行为一致) |
重点配置:responseHandling
responseHandling是 htmx 2.x 中控制“哪些响应码应该被交换、哪些应该视为错误”的核心配置。源码中的默认值(src/htmx.js#L264-L268):
responseHandling: [ { code: '204', swap: false }, { code: '[23]..', swap: true }, { code: '[45]..', swap: false, error: true } ]htmx 收到响应后,会按顺序遍历该数组,把每一项的code当作正则表达式与当前响应码匹配,命中即决定如何处理。可用字段包括code(正则字符串)、swap(是否交换进 DOM)、error(是否视为错误)、ignoreTitle、select、target与swapOverride。
一个典型场景是:后端框架在表单校验失败时返回422,而默认规则[45]..会忽略它。通过 meta 配置即可让 422 被正常交换(示例来自 docs.md):
<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} ] }' />如果希望无论响应码如何都交换内容,可以简化为:
<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' />编程式发起请求:htmx.ajax()
htmx.ajax(verb, path, context)以 htmx 风格发起 AJAX 请求,并返回一个 Promise,可在内容插入 DOM 后执行回调。官方文档给出三种调用签名:
签名一:目标元素
// 向 /example 发起 GET,将响应 HTML 放入 #myDiv htmx.ajax('GET', '/example', '#myDiv') // 内容插入 DOM 后执行(位于 htmx:afterOnLoad 之后、htmx:xhr:loadend 之前) htmx.ajax('GET', '/example', '#myDiv').then(() => { console.log('Content inserted successfully!'); });签名二:选择器字符串(等价于签名一的简写,最终被解析为元素)
签名三:context 上下文对象,可包含以下字段:
| 字段 | 说明 |
|---|---|
source | 请求的源元素,影响请求的hx-*属性将相对该元素及其祖先解析 |
event | “触发”请求的事件 |
handler | 处理响应 HTML 的回调 |
target | 响应要交换进去的目标 |
swap | 响应相对目标的交换方式 |
values | 随请求提交的值 |
headers | 随请求提交的请求头 |
select | 从响应中选择要交换的内容 |
selectOOB | 从响应中选择要做带外(out-of-band)交换的内容 |
push | 为'true'或一个路径,将 URL 推入浏览器历史 |
replace | 为'true'或一个路径,替换浏览器历史中的 URL |
// 向 /example 发起 GET,用 outerHTML 方式把 #myDiv 替换为响应内容 htmx.ajax('GET', '/example', {target:'#myDiv', swap:'outerHTML'})源码视角:请求是如何被发出的
htmx.ajax绑定到ajaxHelper(src/htmx.js#L4111-L4145)。它做了几件关键的事:
- 将 verb 统一转为小写后,委托给
issueAjaxRequest(src/htmx.js#L4319); - 当
context是元素或字符串时,解析出targetOverride;当目标是字符串但无法解析、或提供了source但目标和源都无法解析时,会使用一个DUMMY_ELT(源码中的虚拟<output>元素)作为目标,从而触发htmx:targetError错误事件,避免请求意外替换掉整个 body; issueAjaxRequest内部依次处理hx-confirm确认、hx-sync同步策略(drop / abort 等)、verifyPath同源校验(src/htmx.js#L4166-L4177,受htmx.config.selfRequestsOnly控制)以及htmx:validateUrl事件;- 当
etc.returnPromise为真且环境支持 Promise 时,构造并返回 Promise,其 resolve 时机位于内容成功插入之后。
测试套件 test/core/api.js 对这一行为有大量覆盖:ajax api works、ajax api works by ID、ajax api does not fall back to body when target invalid、ajax api fails when target invalid、ajax returns a promise、ajax api can pass parameters、ajax api push Url should push an element into the cache when true/string等(test/core/api.js#L215-L358)。
DOM 查询助手:find/findAll/closest
这三个方法提供与 jQuery 风格一致的 DOM 查询能力:
// 查找 id 为 my-div 的 div var div = htmx.find("#my-div") // 在该 div 内查找 id 为 another-div 的元素(不含根元素自身) var anotherDiv = htmx.find(div, "#another-div") // 查找所有 div var allDivs = htmx.findAll("div") // 在 #my-div 内查找所有 p 元素 var allParagraphsInMyDiv = htmx.findAll(htmx.find("#my-div"), "p") // 查找 #demo 最近的祖先 div(包含自身) htmx.closest(htmx.find('#demo'), 'div');参数规则:find/findAll既可只传选择器(相对整个文档),也可传“根元素 + 选择器”;closest(elt, selector)返回包含元素自身在内的最近匹配祖先。
源码实现非常直接:find(src/htmx.js#L910-L916)在根元素上调用querySelector,closest(src/htmx.js#L1086-L1092)调用原生elt.closest(selector),且三者都经由resolveTarget(src/htmx.js#L1256-L1262)支持“元素或选择器字符串”的混合传参。对应测试见 test/core/api.js#L11-L38(should find properly、should find all properly、should find closest element properly)。
CSS 类管理:addClass/removeClass/toggleClass/takeClass
// 为 #demo 添加 myClass htmx.addClass(htmx.find('#demo'), 'myClass'); // 1 秒后添加 htmx.addClass(htmx.find('#demo'), 'myClass', 1000); // 移除 myClass;6 秒后移除 htmx.removeClass(htmx.find("#my-div"), "myClass"); htmx.removeClass(htmx.find("#my-div"), "myClass", 6000); // 切换 selected 类 htmx.toggleClass(htmx.find("#tab2"), "selected"); // 把 selected 类从 tab2 的所有兄弟元素上拿走,仅保留在 tab2 上 htmx.takeClass(htmx.find("#tab2"), "selected");源码实现细节值得注意:
addClassToElement(src/htmx.js#L1003-L1016):若传入延迟,先setTimeout再递归调用;若元素解析失败则静默返回(elt = asElement(resolveTarget(elt))后判空)。removeClassFromElement(src/htmx.js#L1027-L1046):当元素最后一个类被移除、classList长度归零时,会额外删除class属性本身,保证 DOM 干净。takeClassForElement(src/htmx.js#L1069-L1075):遍历elt.parentElement.children逐一移除该类,再给目标元素加上,实现了典型的“tab 高亮互斥”效果。
对应测试覆盖了带选择器传参、延迟添加/移除以及“对非法元素移除类不报错”等边界场景(test/core/api.js#L69-L200)。
DOM 操作:remove/swap
htmx.remove(elt[, delay])
从 DOM 中移除元素,支持元素或选择器字符串,可选延迟毫秒数:
// 立即移除 htmx.remove(htmx.find("#my-div")); // 2 秒后移除 htmx.remove(htmx.find("#my-div"), 2000);源码removeElement(src/htmx.js#L950-L960)先resolveTarget,有延迟则setTimeout后递归,否则直接parentElt(elt).removeChild(elt)。
htmx.swap(target, content, swapSpec[, swapOptions])
执行 HTML 内容的交换与沉降(settling),是hx-swap属性的编程式等价物。swapSpec对应hx-swap的参数集:
swapStyle(必填)——交换方式:innerHTML、outerHTML、beforebegin、afterbegin、beforeend、afterend、delete、none等;swapDelay/settleDelay(number)——交换与沉降前的延迟;transition(bool)——是否使用视图过渡;ignoreTitle(bool)——禁用页面标题更新;head(string)——head标签处理策略(merge或append),留空则禁用 head 处理;scroll/scrollTarget/show/showTarget/focusScroll——交换后的滚动处理。
swapOptions是额外的可选参数:
select——要交换内容的选取器(等价于hx-select);selectOOB——带外交换内容的选取器(等价于hx-select-oob);eventInfo——附加到htmx:afterSwap与htmx:afterSettle事件上的对象;anchor——触发滚动的锚点元素,沉降时滚动进视口;contextElement——交换操作的上下文 DOM 元素,用于查找该元素启用的扩展;afterSwapCallback/afterSettleCallback——交换后、沉降后调用的回调(无参数)。
// 将 #output 的 innerHTML 替换为包含 "Swapped!" 的 div htmx.swap("#output", "<div>Swapped!</div>", {swapStyle: 'innerHTML'});源码swap(src/htmx.js#L1924)是一个完整的交换管线:解析目标 → 处理 OOB 与selectOOB→ 处理hx-partial局部交换 → 主交换(swapWithStyle)→ 恢复焦点与选区 → 触发htmx:afterSwap→ 处理标题 → 执行沉降任务并触发htmx:afterSettle。它还特别支持textContent交换方式(不做 HTML 解析,直接插入文本)。测试覆盖了基础交换、交换延迟、View Transition、select/selectOOB、outerHTML等场景(test/core/api.js#L492-L565)。
事件处理:on/off/trigger/onLoad
添加与移除监听
htmx.on()与htmx.off()都支持两种调用形态:省略 target 时默认绑定到body,或显式指定目标元素:
// 在 body 上监听 click var myEventListener = htmx.on("click", function(evt){ console.log(evt); }); // 在 #my-div 上监听 click var myEventListener = htmx.on("#my-div", "click", function(evt){ console.log(evt); }); // 仅触发一次 var myEventListener = htmx.on("#my-div", "click", function(evt){ console.log(evt); }, { once: true }); // 移除监听 htmx.off("click", myEventListener); htmx.off("#my-div", "click", myEventListener)on的第四个可选参数可以是 addEventListener 的 options 对象(如{ once: true }、{ capture: true })或 useCapture 布尔值。
源码addEventListenerImpl(src/htmx.js#L1312-L1319)把注册推迟到 DOM ready 之后执行,并返回 listener 本身,方便后续off移除;参数归一化由processEventArgs(src/htmx.js#L1283-L1299)完成——当第二个参数是函数时,自动把第一个参数当作事件名、目标缺省为document.body。
触发事件
// 在 #tab2 上触发 myEvent 事件,携带 detail {answer:42} htmx.trigger("#tab2", "myEvent", {answer:42});triggerEvent(src/htmx.js#L3156-L3180)的行为比普通dispatchEvent更丰富:
- detail 缺省时自动补
{},并始终注入detail.elt(被触发元素); - 事件以
bubbles: true, cancelable: true, composed: true构造,可穿透 Shadow DOM; - camelCase 事件名会自动以 kebab-case 再触发一次(如
myEvent同时派发my-event),保证两种命名风格都能被监听; - 若
htmx.logger已设置,触发前会调用 logger 记录; - 最后会遍历元素上启用的扩展,逐一调用其
onEvent钩子。
htmx.onLoad(callback)
为htmx:load事件添加回调,用于处理新加载的内容,例如初始化第三方库:
htmx.onLoad(function(elt){ MyLibrary.init(elt); })源码onLoadHelper(src/htmx.js#L877-L882)本质上就是htmx.on('htmx:load', ...)的包装,把事件 detail 中的elt传给回调。htmx 的全部事件清单可查阅 events.md(含htmx:beforeRequest、htmx:afterRequest、htmx:beforeSwap、htmx:afterSwap、htmx:afterSettle、htmx:confirm等),是编写事件驱动逻辑时的权威参考。
内容处理:htmx.process(elt)
当内容由非 htmx 请求周期的途径加入 DOM(例如手动设置innerHTML)时,htmx 属性不会自动生效,此时需要显式调用process让 htmx 接管:
document.body.innerHTML = "<div hx-get='/example'>Get it!</div>" // 处理新加入的内容,使 hx-get 生效 htmx.process(document.body);源码processNode(src/htmx.js#L3056-L3079)会先解析目标、检查disableSelector禁用状态,然后收集需要初始化的元素(含hx-on通配符元素),逐一调用initNode完成监听器与行为的安装。
值解析:htmx.values(elt[, type])
返回给定元素经 htmx 值解析机制得到的输入值对象。第二个参数是请求类型(如get/post),非 GET 请求会包含元素所在的外层表单,默认为post:
// 获取该表单关联的值 var values = htmx.values(htmx.find("#myForm"));其底层是getInputValues(src/htmx.js#L3656-L3707),实现要点包括:基于FormData收集;非 GET 请求额外处理关联表单;考虑lastButtonClicked(被点击的提交按钮的 name/value);纳入hx-include指定的元素及其内部输入;表单值优先级高于普通值(overrideFormData)。测试中的values api returns formDataProxy with correct form data even if clicked button removed(test/core/api.js#L567)验证了按钮被移除后值解析依然正确。
调试工具:logAll/logNone/logger
htmx.logAll(); // 记录所有 htmx 事件,便于调试 htmx.logNone(); // 关闭日志自定义日志记录器:
htmx.logger = function(elt, event, data) { if(console) { console.log("INFO:", event, elt, data); } }源码中logAll(src/htmx.js#L889-L895)就是把htmx.logger设置为向 console 输出的默认函数;logNone(src/htmx.js#L897-L899)将logger置为null。logger 的调用点在triggerEvent中(src/htmx.js#L3163-L3165),且htmx:afterProcessNode事件会被跳过以免日志刷屏(ignoreEventForLogging)。
定时解析:htmx.parseInterval(str)
以 htmx 一致的方式解析时间间隔字符串,对带定时属性的扩展插件非常有用:
// 返回 3000 var milliseconds = htmx.parseInterval("3s"); // 官方文档示例提示此处返回 3 —— 注意:见下方源码行为说明 var milliseconds = htmx.parseInterval("3m");官方文档给出的“Caution”是:只接受s或ms后缀,其余值走parseFloat。但查阅当前仓库源码 src/htmx.js#L373-L389 可以发现,实际实现比文档描述更进一步:ms后缀按毫秒解析、s后缀乘以 1000、m后缀乘以 60000(即 3 分钟 = 180000 毫秒),其余情况使用parseFloat;无法解析时返回undefined而非 NaN。因此文档示例中的“3m返回 3”在当前版本源码中已不再成立,应以源码行为为准——这也提醒扩展作者:写计时属性时优先使用s/ms后缀。
扩展开发:defineExtension/removeExtension/createEventSource/createWebSocket
注册与注销扩展
// 定义一个"傻乎乎"的扩展:记录所有触发的事件名 htmx.defineExtension("silly", { onEvent : function(name, evt) { console.log("Event " + name + " was triggered!") } }); // 移除扩展 htmx.removeExtension("my-extension");源码defineExtension(src/htmx.js#L5055-L5060)在扩展定义含init时,会以internalAPI(内部辅助函数集合,见 src/htmx.js#L324-L352)调用它完成初始化,然后把扩展与基类合并后登记到extensions注册表;removeExtension(src/htmx.js#L5069-L5071)则直接删除注册项。扩展如何被元素启用(向上遍历hx-ext属性、支持ignore:前缀)见getExtensions(src/htmx.js#L5081-L5108)。更系统的扩展编写指南可参考 extensions 文档 与 building.md。
覆盖 SSE 与 WebSocket 工厂
htmx.createEventSource与htmx.createWebSocket是两个可被覆盖的属性,分别负责创建 Server-Sent Events 与 WebSocket 连接,用于自定义建立方式:
// 覆盖 SSE 工厂:不使用凭证 htmx.createEventSource = function(url) { return new EventSource(url, {withCredentials:false}); }; // 覆盖 WebSocket 工厂:使用指定协议 htmx.createWebSocket = function(url) { return new WebSocket(url, ['wss']); };createEventSource的签名是func(url),返回一个新的EventSource;createWebSocket的签名同为func(url),返回一个新的WebSocket。相关的重连与二进制类型行为则由前面提到的htmx.config.wsReconnectDelay与htmx.config.wsBinaryType控制。
类型声明与测试:给你的代码加上类型安全
如果项目使用 TypeScript,仓库提供的 src/htmx.esm.d.ts 完整声明了全部 API 的类型:htmx命名空间下的每个方法签名(L155-L219)、HtmxSwapStyle、HtmxSwapSpecification、HtmxAjaxHelperContext、HtmxRequestConfig、HtmxResponseHandlingConfig、HtmxExtension等类型均可在其中找到,是判断参数合法性、避免低级错误的第一手依据。
与此同时,仓库的浏览器测试套件 test/core/api.js 对上述 API 的行为做了系统性验证,覆盖查询、类操作、AJAX 目标解析与 Promise、push/replace历史操作、swap各模式、onLoad、trigger、values、process、logAll/logNone等场景。阅读这些用例(例如ajax api does not fall back to body when target invalid、swap outerHTML works when parent is removed)能帮助你理解 API 的边界行为,是编写可靠业务代码的最佳参考。
小结
htmx 的 JavaScript API 虽然精简,却是连接声明式属性与命令式逻辑的桥梁:htmx.config让你以编程或 meta 标签方式全局调优;htmx.ajax与htmx.swap覆盖了请求与交换的完整生命周期;on/off/trigger/onLoad提供与 htmx 事件体系一致的事件管理;find/findAll/closest、类操作与values补齐了 DOM 操作和表单取值的日常需求;而defineExtension等入口则支撑起整个扩展生态。掌握这套 API,即可在保持 htmx 优雅声明式风格的同时,获得与框架内部机制完全一致的编程控制能力。
- 前端
【免费下载链接】htmx
htmx - high power tools for HTML
相关推荐
LMCache Internal API Server 完全指南:运行时管理接口的配置、路由发现与扩展
LMCache Internal API Server 完全指南:运行时管理接口的配置、路由发现与扩展 LMCache 的 Internal API Serve
人工智能大模型缓存抽象模型推理服务Apache Druid JavaScript 扩展开发指南:运行时动态扩展的七类能力与安全实践
Apache Druid JavaScript 扩展开发指南:运行时动态扩展的七类能力与安全实践 Apache Druid 允许通过 JavaScript 在运
数据库OLAP大数据后端TVM Relay TensorRT 集成实战:BYOC 编译、运行时配置与算子扩展指南
TVM Relay TensorRT 集成实战:BYOC 编译、运行时配置与算子扩展指南 本指南围绕 TVM 中 Relay 与 NVIDIA TensorRT
编译器深度学习模型优化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考