触发器与修饰符
触发器与修饰符
默认情况下,hx-get 和 hx-post 在元素的 click 事件上触发。但在实际应用中,你需要更精细地控制请求时机:输入时实时搜索、滚动到底部加载更多、鼠标悬停预览内容等。htmx 的 hx-trigger 属性提供了完整的事件触发控制体系。
hx-trigger 基础语法
hx-trigger 的值是一个逗号分隔的事件声明列表,每个声明可以包含事件名和若干修饰符:
<input hx-get="/api/search"
hx-target="#results"
hx-trigger="keyup changed delay:500ms" />这个触发器表示:当 keyup 事件发生,且输入值相对于上次发生了变化(changed),在延迟 500 毫秒后(delay:500ms)发起请求。
标准事件触发
鼠标事件
<!-- 点击触发(默认) -->
<button hx-get="/api/click">点击加载</button>
<!-- 双击触发 -->
<div hx-get="/api/edit" hx-trigger="dblclick">双击编辑</div>
<!-- 鼠标进入触发 -->
<div hx-get="/api/preview" hx-trigger="mouseenter once">
悬停预览(仅触发一次)
</div>
<!-- 鼠标离开触发 -->
<div hx-get="/api/save-draft" hx-trigger="mouseleave delay:2s">
离开 2 秒后自动保存草稿
</div>表单事件
<!-- 值变化触发 -->
<select hx-get="/api/filter" hx-trigger="change" hx-target="#list">
<option>全部</option>
<option>进行中</option>
<option>已完成</option>
</select>
<!-- 获得焦点触发 -->
<input hx-get="/api/suggestions" hx-trigger="focus" hx-target="#suggestions" />
<!-- 失去焦点且值变化时触发 -->
<input hx-post="/api/validate-email" hx-trigger="blur changed" />键盘事件
<!-- 任意键盘输入触发 -->
<input hx-get="/api/search" hx-trigger="keyup" hx-target="#results" />
<!-- 仅回车键触发 -->
<input hx-post="/api/submit" hx-trigger="keyup[key=='Enter']" />
<!-- 修饰键组合 -->
<div hx-get="/api/shortcut" hx-trigger="keydown[ctrlKey&&key=='k']">
按 Ctrl+K 触发
</div>提示:
keyup[key=='Enter']是事件过滤器语法,只有当条件为真时事件才会触发请求。
htmx 特殊触发事件
除了标准 DOM 事件,htmx 还定义了一组特殊事件,用于处理常见的交互模式:
load:元素加载时触发
<div hx-get="/api/stats" hx-trigger="load" hx-target="this">
页面加载时自动获取数据
</div>revealed:元素进入视口时触发
<div hx-get="/api/more" hx-trigger="revealed" hx-swap="outerHTML">
滚动到此元素时加载更多
</div>revealed 是实现无限滚动的核心事件。当元素首次进入浏览器视口时触发一次请求。
intersect:交叉观察器触发
intersect 基于 IntersectionObserver API,比 revealed 更灵活:
<div hx-get="/api/lazy-image"
hx-trigger="intersect once"
hx-swap="outerHTML">
<div class="placeholder">加载中...</div>
</div>intersect once 确保只在首次进入视口时触发。
every:定时轮询
<div hx-get="/api/notifications" hx-trigger="every 30s" hx-target="this">
每 30 秒刷新通知
</div>注意:
every会无限轮询,直到元素从 DOM 中移除。确保设置合理的间隔,并在不需要时移除元素。
修饰符详解
修饰符附加在事件名之后,进一步控制触发行为。
once:仅触发一次
<button hx-get="/api/setup" hx-trigger="click once">
初始化(只能点击一次)
</button>触发一次后,htmx 自动移除该元素上的请求处理。
changed:值变化时才触发
<input hx-get="/api/search" hx-trigger="keyup changed delay:300ms" />changed 修饰符确保只有输入值真正改变时才发起请求。快速输入相同的字符不会触发重复请求。
提示:
changed常与keyup配合使用,避免在导航键(方向键、Home、End)上浪费请求。
delay:延迟触发
<input hx-get="/api/search" hx-trigger="keyup delay:500ms" />用户停止输入 500 毫秒后才发起请求。如果在 500 毫秒内再次输入,计时器重置。这是实现搜索防抖的标准方式。
throttle:节流触发
<div hx-get="/api/scroll-position"
hx-trigger="scroll throttle:250ms"
hx-target="this">
滚动事件节流
</div>与 delay 不同,throttle 保证在指定间隔内最多触发一次。适合高频事件如滚动、窗口调整大小。
| 修饰符 | 行为 | 适用场景 |
|---|---|---|
delay | 等待静默期后触发,期间重置计时器 | 搜索输入 |
throttle | 固定间隔最多触发一次 | 滚动、resize |
from:指定事件源
<div hx-get="/api/update" hx-trigger="click from:#reload-btn">
点击 #reload-btn 时更新此区域
</div>
<button id="reload-btn">刷新</button>from 允许一个元素监听另一个元素的事件。这对于将操作按钮与内容区域分离的布局非常有用。
<!-- 文档级事件监听 -->
<div hx-get="/api/shortcut-action"
hx-trigger="keydown[key=='Escape'] from:body">
按 ESC 键触发
</div>target:过滤事件目标
<ul hx-delete="/api/item" hx-trigger="click target:.delete-btn" hx-swap="delete">
<li>项目 1 <button class="delete-btn">删除</button></li>
<li>项目 2 <button class="delete-btn">删除</button></li>
</ul>只有点击 .delete-btn 时才触发,点击列表项的其他部分不会触发。
consume:阻止事件冒泡
<div hx-get="/api/detail" hx-trigger="click consume">
点击加载详情(阻止冒泡到父元素)
</div>consume 阻止事件继续冒泡,防止触发父元素上的相同事件监听。
queue:控制请求队列
当用户快速触发多次请求时,queue 修饰符控制如何处理排队的请求:
| 值 | 行为 |
|---|---|
first | 保留第一个请求,丢弃后续请求 |
last | 丢弃待处理的请求,保留最新的 |
all | 排队所有请求,按顺序执行 |
none | 如果有待处理请求,不发起新请求 |
<button hx-post="/api/vote"
hx-trigger="click queue:none"
hx-target="this"
hx-swap="outerHTML">
投票
</button>queue:none 确保在请求处理期间重复点击不会产生额外的投票请求。
<input hx-get="/api/search"
hx-trigger="keyup delay:300ms queue:last"
hx-target="#results" />queue:last 在快速输入时,如果有待处理的搜索请求,取消它并发起最新的请求。
多事件触发器
一个元素可以配置多个触发条件:
<input hx-get="/api/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#results" />这个输入框会在两种情况下触发搜索:
- 键盘输入且值改变后 500 毫秒
- 触发 HTML5
search事件时(如点击搜索框的清除按钮)
事件过滤器
使用方括号可以在事件触发前进行条件过滤:
键盘过滤
<!-- 仅在回车键时触发 -->
<input hx-post="/api/submit" hx-trigger="keyup[key=='Enter']" />
<!-- 仅在输入字母时触发 -->
<input hx-get="/api/suggest" hx-trigger="keyup[keyCode>=65&&keyCode<=90]" />
<!-- 忽略方向键 -->
<input hx-get="/api/search"
hx-trigger="keyup[key!='ArrowUp'&&key!='ArrowDown'&&key!='ArrowLeft'&&key!='ArrowRight']" />鼠标过滤
<!-- 仅左键点击 -->
<button hx-get="/api/action" hx-trigger="click[button==0]">
左键点击
</button>
<!-- 仅主按钮点击(考虑 RTL 布局) -->
<button hx-get="/api/action" hx-trigger="click[button==0&&ctrlKey]">
Ctrl + 左键
</button>状态过滤
<!-- 仅在元素可见时触发 -->
<div hx-get="/api/update" hx-trigger="every 10s[visible()]">
每 10 秒更新(仅在可见时)
</div>触发器继承
子元素可以继承父元素的触发器配置,但通常会被自身的 hx-trigger 覆盖。
更常见的模式是使用 hx-boost 提升整个区域的交互:
<div hx-boost="true">
<!-- 此区域内的所有链接和表单自动变为 AJAX 请求 -->
<a href="/page/1">第一页</a>
<a href="/page/2">第二页</a>
<form action="/search" method="get">
<input type="text" name="q" />
<button type="submit">搜索</button>
</form>
</div>hx-boost="true" 将传统的链接和表单提升为 htmx AJAX 请求,无需为每个元素单独添加属性。
完整示例:实时搜索
<div class="search-box">
<input type="search"
name="q"
placeholder="搜索文章..."
hx-get="/api/search"
hx-trigger="keyup changed delay:400ms, search"
hx-target="#search-results"
hx-indicator=".search-spinner"
hx-vals='{"limit": "10"}'
minlength="2" />
<span class="search-spinner htmx-indicator">🔍</span>
</div>
<div id="search-results"></div>| 配置 | 作用 |
|---|---|
keyup changed delay:400ms | 输入停止 400ms 后,且值有变化时搜索 |
search | 支持搜索框的清除按钮触发 |
hx-indicator | 显示加载动画 |
hx-vals | 固定每页 10 条结果 |
minlength="2" | 原生 HTML 验证,至少 2 个字符 |
完整示例:无限滚动 + 加载更多
<div id="feed">
<article>文章 1</article>
<article>文章 2</article>
</div>
<!-- 方式一:revealed 自动触发 -->
<div hx-get="/api/more?page=3"
hx-trigger="revealed"
hx-target="#feed"
hx-swap="beforeend"
hx-select="article"
hx-indicator=".loading">
<div class="loading htmx-indicator">加载更多...</div>
</div>
<!-- 方式二:手动加载 + 自动加载组合 -->
<button id="load-more"
hx-get="/api/more"
hx-target="#feed"
hx-swap="beforeend"
hx-select="article"
hx-indicator=".loading"
hx-vals="js:{page: document.querySelectorAll('#feed article').length / 10 + 1}">
加载更多
</button>
<script>
// 自动触发按钮点击实现无限滚动
document.addEventListener('scroll', () => {
const btn = document.getElementById('load-more');
if (!btn) return;
const rect = btn.getBoundingClientRect();
if (rect.top < window.innerHeight + 200) {
btn.click();
}
});
</script>总结
hx-trigger 是 htmx 中最灵活、最强大的属性之一,它让你能够精确控制请求的时机和条件:
- 标准事件:
click、change、keyup、focus、blur等 DOM 事件 - htmx 特殊事件:
load、revealed、intersect、every应对常见交互模式 - 修饰符:
once、changed、delay、throttle、from、target、consume、queue精细化控制触发行为 - 事件过滤器:方括号条件过滤,实现复杂的触发逻辑
- 多事件组合:逗号分隔多个触发条件
掌握触发器与修饰符后,你可以用声明式的方式实现几乎所有常见的交互模式,无需编写事件监听代码。下一篇文章将进入进阶技能,学习 表单处理与验证。
评论
Written by
AI-Writer