htmx

触发器与修饰符

By AI-Writer 7 min read

触发器与修饰符

默认情况下,hx-gethx-post 在元素的 click 事件上触发。但在实际应用中,你需要更精细地控制请求时机:输入时实时搜索、滚动到底部加载更多、鼠标悬停预览内容等。htmx 的 hx-trigger 属性提供了完整的事件触发控制体系。

hx-trigger 基础语法

hx-trigger 的值是一个逗号分隔的事件声明列表,每个声明可以包含事件名和若干修饰符:

html
<input hx-get="/api/search"
       hx-target="#results"
       hx-trigger="keyup changed delay:500ms" />

这个触发器表示:当 keyup 事件发生,且输入值相对于上次发生了变化changed),在延迟 500 毫秒后(delay:500ms)发起请求。

标准事件触发

鼠标事件

html
<!-- 点击触发(默认) -->
<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>

表单事件

html
<!-- 值变化触发 -->
<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" />

键盘事件

html
<!-- 任意键盘输入触发 -->
<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:元素加载时触发

html
<div hx-get="/api/stats" hx-trigger="load" hx-target="this">
    页面加载时自动获取数据
</div>

revealed:元素进入视口时触发

html
<div hx-get="/api/more" hx-trigger="revealed" hx-swap="outerHTML">
    滚动到此元素时加载更多
</div>

revealed 是实现无限滚动的核心事件。当元素首次进入浏览器视口时触发一次请求。

intersect:交叉观察器触发

intersect 基于 IntersectionObserver API,比 revealed 更灵活:

html
<div hx-get="/api/lazy-image"
     hx-trigger="intersect once"
     hx-swap="outerHTML">
    <div class="placeholder">加载中...</div>
</div>

intersect once 确保只在首次进入视口时触发。

every:定时轮询

html
<div hx-get="/api/notifications" hx-trigger="every 30s" hx-target="this">
    每 30 秒刷新通知
</div>

注意every 会无限轮询,直到元素从 DOM 中移除。确保设置合理的间隔,并在不需要时移除元素。

修饰符详解

修饰符附加在事件名之后,进一步控制触发行为。

once:仅触发一次

html
<button hx-get="/api/setup" hx-trigger="click once">
    初始化(只能点击一次)
</button>

触发一次后,htmx 自动移除该元素上的请求处理。

changed:值变化时才触发

html
<input hx-get="/api/search" hx-trigger="keyup changed delay:300ms" />

changed 修饰符确保只有输入值真正改变时才发起请求。快速输入相同的字符不会触发重复请求。

提示changed 常与 keyup 配合使用,避免在导航键(方向键、Home、End)上浪费请求。

delay:延迟触发

html
<input hx-get="/api/search" hx-trigger="keyup delay:500ms" />

用户停止输入 500 毫秒后才发起请求。如果在 500 毫秒内再次输入,计时器重置。这是实现搜索防抖的标准方式。

throttle:节流触发

html
<div hx-get="/api/scroll-position"
     hx-trigger="scroll throttle:250ms"
     hx-target="this">
    滚动事件节流
</div>

delay 不同,throttle 保证在指定间隔内最多触发一次。适合高频事件如滚动、窗口调整大小。

修饰符行为适用场景
delay等待静默期后触发,期间重置计时器搜索输入
throttle固定间隔最多触发一次滚动、resize

from:指定事件源

html
<div hx-get="/api/update" hx-trigger="click from:#reload-btn">
    点击 #reload-btn 时更新此区域
</div>

<button id="reload-btn">刷新</button>

from 允许一个元素监听另一个元素的事件。这对于将操作按钮与内容区域分离的布局非常有用。

html
<!-- 文档级事件监听 -->
<div hx-get="/api/shortcut-action"
     hx-trigger="keydown[key=='Escape'] from:body">
    按 ESC 键触发
</div>

target:过滤事件目标

html
<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:阻止事件冒泡

html
<div hx-get="/api/detail" hx-trigger="click consume">
    点击加载详情(阻止冒泡到父元素)
</div>

consume 阻止事件继续冒泡,防止触发父元素上的相同事件监听。

queue:控制请求队列

当用户快速触发多次请求时,queue 修饰符控制如何处理排队的请求:

行为
first保留第一个请求,丢弃后续请求
last丢弃待处理的请求,保留最新的
all排队所有请求,按顺序执行
none如果有待处理请求,不发起新请求
html
<button hx-post="/api/vote"
        hx-trigger="click queue:none"
        hx-target="this"
        hx-swap="outerHTML">
    投票
</button>

queue:none 确保在请求处理期间重复点击不会产生额外的投票请求。

html
<input hx-get="/api/search"
       hx-trigger="keyup delay:300ms queue:last"
       hx-target="#results" />

queue:last 在快速输入时,如果有待处理的搜索请求,取消它并发起最新的请求。

多事件触发器

一个元素可以配置多个触发条件:

html
<input hx-get="/api/search"
       hx-trigger="keyup changed delay:500ms, search"
       hx-target="#results" />

这个输入框会在两种情况下触发搜索:

  1. 键盘输入且值改变后 500 毫秒
  2. 触发 HTML5 search 事件时(如点击搜索框的清除按钮)

事件过滤器

使用方括号可以在事件触发前进行条件过滤:

键盘过滤

html
<!-- 仅在回车键时触发 -->
<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']" />

鼠标过滤

html
<!-- 仅左键点击 -->
<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>

状态过滤

html
<!-- 仅在元素可见时触发 -->
<div hx-get="/api/update" hx-trigger="every 10s[visible()]">
    每 10 秒更新(仅在可见时)
</div>

触发器继承

子元素可以继承父元素的触发器配置,但通常会被自身的 hx-trigger 覆盖。

更常见的模式是使用 hx-boost 提升整个区域的交互:

html
<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 请求,无需为每个元素单独添加属性。

完整示例:实时搜索

html
<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 个字符

完整示例:无限滚动 + 加载更多

html
<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 中最灵活、最强大的属性之一,它让你能够精确控制请求的时机和条件:

  • 标准事件clickchangekeyupfocusblur 等 DOM 事件
  • htmx 特殊事件loadrevealedintersectevery 应对常见交互模式
  • 修饰符oncechangeddelaythrottlefromtargetconsumequeue 精细化控制触发行为
  • 事件过滤器:方括号条件过滤,实现复杂的触发逻辑
  • 多事件组合:逗号分隔多个触发条件

掌握触发器与修饰符后,你可以用声明式的方式实现几乎所有常见的交互模式,无需编写事件监听代码。下一篇文章将进入进阶技能,学习 表单处理与验证

#htmx #hx-trigger #events #modifiers

评论

A

Written by

AI-Writer

Related Articles

htmx
#4

触发器与修饰符

深入掌握 hx-trigger 的完整语法体系,学习事件类型、过滤器、延迟、轮询、可见性触发及各种修饰符的使用场景

Read More
htmx
#12

自定义扩展开发

深入掌握 htmx.defineExtension API,学习创建自定义行为扩展,理解扩展的生命周期钩子与钩子函数

Read More