目录
htmx 是一个库,它允许你直接从 HTML 访问现代浏览器功能,而不需要使用 JavaScript。
为了理解 htmx,首先让我们看看一个锚标签:
<a href="/blog">博客</a>
这个锚标签告诉浏览器:
“当用户点击这个链接时,向 ‘/blog’ 发出一个 HTTP GET 请求,并将响应内容加载到浏览器窗口中。”
基于此,考虑以下这段 HTML:
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML"
>
点击我!
</button>
这告诉 htmx:
“当用户点击这个按钮时,向 ‘/clicked’ 发出一个 HTTP POST 请求,并使用响应中的内容替换 DOM 中 ID 为
parent-div
的元素。”
htmx 扩展并概括了 HTML 作为超文本的核心理念,直接在语言中开辟了更多可能性:
GET
和 POST
,都可以使用注意,当你使用 htmx 时,通常在服务器端响应的是 HTML,而不是 JSON。这让你完全处于 原始的 web 编程模型 中,使用 超文本作为应用状态引擎,甚至不需要真正理解这个概念。
值得一提的是,如果你愿意,你可以在使用 htmx 时使用 data-
前缀:
<a data-hx-post="/click">点击我!</a>
最后,htmx 版本 1 仍然支持并支持 IE11。
如果你从 htmx 1.x 迁移到 htmx 2.x,请参阅 htmx 1.x 迁移指南。
如果你是从 intercooler.js 迁移到 htmx,请参阅 intercooler 迁移指南。
Htmx 是一个无依赖的、面向浏览器的 JavaScript 库。这意味着使用它就像在文档头部添加一个 <script>
标签一样简单。无需构建系统即可使用它。
最快的使用 htmx 的方式是通过 CDN 加载。你只需将以下代码添加到你的头标签中即可开始使用:
<script src="https://unpkg.com/htmx.org@2.0.2" integrity="sha384-Y7hw+L/jvKeWIRRkqWYfPcvVxHzVzn5REgzbawhxAuQGwX1XWe70vji+VSeHOThJ" crossorigin="anonymous"></script>
还有一个未压缩的版本可以用于调试:
<script src="https://unpkg.com/htmx.org@2.0.2/dist/htmx.js" integrity="sha384-yZq+5izaUBKcRgFbxgkRYwpHhHHCpp5nseXp0MEQ1A4MTWVMnqkmcuFez8x5qfxr" crossorigin="anonymous"></script>
虽然 CDN 方式极其简单,但你可能需要考虑不在生产环境中使用 CDN。
下一个最简单的安装 htmx 的方式是直接将其复制到你的项目中。
从 unpkg.com 下载 htmx.min.js
,并将其添加到项目中的适当目录中,然后使用 <script>
标签在需要的地方包含它:
<script src="/path/to/htmx.min.js"></script>
对于 npm 风格的构建系统,你可以通过 npm 安装 htmx:
npm install htmx.org@2.0.2
安装后,你需要使用适当的工具来使用 node_modules/htmx.org/dist/htmx.js
(或 .min.js
)。例如,你可能会将 htmx 与一些扩展和项目特定代码捆绑在一起。
如果你使用 webpack 来管理你的 JavaScript:
htmx
index.js
中import 'htmx.org';
如果你想使用全局 htmx
变量(推荐),你需要将其注入到 window 作用域:
index.js
中(位于第 2 步中的导入下面)import 'path/to/my_custom.js';
window.htmx = require('htmx.org');
htmx 的核心是一组允许你直接从 HTML 发出 AJAX 请求的属性:
属性 | 描述 |
---|---|
hx-get | 向指定 URL 发出 GET 请求 |
hx-post | 向指定 URL 发出 POST 请求 |
hx-put | 向指定 URL 发出 PUT 请求 |
hx-patch | 向指定 URL 发出 PATCH 请求 |
hx-delete | 向指定 URL 发出 DELETE 请求 |
这些属性中的每一个都接受一个 URL,以便发出 AJAX 请求。元素将在被 触发 时向指定的 URL 发出指定类型的请求:
<button hx-put="/messages">
发送 PUT 请求到 Messages
</button>
这告诉浏览器:
当用户点击这个按钮时,向 URL /messages 发出一个 PUT 请求,并将响应加载到按钮中
默认情况下,AJAX 请求由元素的“自然”事件触发:
input
、textarea
和 select
在 change
事件时触发form
在 submit
事件时触发click
事件时触发如果你想要不同的行为,你可以使用 hx-trigger 属性来
指定哪个事件将导致请求。
下面是一个 div
,当鼠标进入时,它会向 /mouse_entered
发出 post 请求:
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[小老鼠,快进来!]
</div>
触发器还可以有一些额外的修饰符来改变其行为。例如,如果你希望请求只发生一次,你可以为触发器使用 once
修饰符:
<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
[小老鼠,快进来!]
</div>
触发器可以使用的其他修饰符包括:
changed
- 仅在元素的值发生变化时发出请求delay:<时间间隔>
- 在发出请求之前等待给定的时间(例如 1s
)。如果事件再次触发,则重新开始计时。throttle:<时间间隔>
- 在发出请求之前等待给定的时间(例如 1s
)。与 delay
不同,如果在时间限制到达之前发生新事件,则该事件将被丢弃,因此请求将在时间段结束时触发。from:<CSS 选择器>
- 在不同的元素上监听事件。这可以用于诸如键盘快捷键之类的功能。请注意,如果页面发生更改,此 CSS 选择器不会重新计算。你可以使用这些属性来实现许多常见的用户体验模式,例如 主动搜索:
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="搜索..."
>
<div id="search-results"></div>
这个输入框将在键盘按键抬起事件后 500 毫秒发出请求,如果输入已更改,则将结果插入到 ID 为 search-results
的 div
中。
在 hx-trigger 属性中可以指定多个触发器,使用逗号分隔。
你也可以通过在事件名称后使用方括号来应用触发器过滤器,括号内包含一个 JavaScript 表达式,该表达式将被评估。如果表达式评估为 true
,则事件将触发,否则不会触发。
以下是一个仅在按住 Control 键点击元素时触发的示例:
<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Control Click Me
</div>
像 ctrlKey
这样的属性将首先根据触发事件进行解析,然后才是全局范围。this
符号将被设置为当前元素。
htmx 提供了一些特殊事件供在 hx-trigger 中使用:
load
- 元素首次加载时触发一次revealed
- 元素首次滚动进入视口时触发一次intersect
- 元素首次与视口相交时触发一次。此事件支持两个附加选项:
root:<selector>
- 用于指定交叉观察器的根元素的 CSS 选择器threshold:<float>
- 一个介于 0.0 和 1.0 之间的浮点数,表示触发事件的交叉比例你还可以使用自定义事件来触发请求,如果你有高级用例的话。
如果你希望元素定期请求指定的 URL 而不是等待事件,可以使用 hx-trigger
属性中的 every
语法:
<div hx-get="/news" hx-trigger="every 2s"></div>
这告诉 htmx:
每隔 2 秒,向 /news 发起一个 GET 请求,并将响应加载到 div 中
如果你想在服务器响应时停止轮询,你可以使用 HTTP 响应码 286
,元素将取消轮询。
在 htmx 中实现轮询的另一种技术是“加载轮询”,即元素指定一个 load
触发器和一个延迟,并用响应替换自身:
<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML"
>
</div>
如果 /messages
端点持续返回这样设置的 div,它将每秒“轮询”一次该 URL。
加载轮询在某些情况下非常有用,例如轮询有一个终点,在到达终点时轮询终止,比如当你在向用户展示一个进度条时。
当发出 AJAX 请求时,通常让用户知道某些事情正在发生是很有必要的,因为浏览器不会给他们任何反馈。你可以通过使用 htmx-indicator
类在 htmx 中实现这一点。
htmx-indicator
类被定义为默认情况下任何具有此类的元素的不透明度为 0,使其在 DOM 中不可见但存在。
当 htmx 发出请求时,它会在某个元素(无论是请求元素还是指定的其他元素)上添加 htmx-request
类。htmx-request
类将使具有 htmx-indicator
类的子元素的不透明度过渡到 1,从而显示指示器。
<button hx-get="/click">
Click Me!
<img class="htmx-indicator" src="/spinner.gif">
</button>
这里我们有一个按钮。当点击它时,htmx-request
类将被添加到它上面,从而显示 spinner gif 元素。(我现在更喜欢使用 SVG 转轮。)
虽然 htmx-indicator
类使用不透明度来隐藏和显示进度指示器,但如果你更喜欢其他机制,你可以像这样创建自己的 CSS 过渡:
.htmx-indicator{
display:none;
}
.htmx-request .htmx-indicator{
display:inline;
}
.htmx-request.htmx-indicator{
display:inline;
}
如果你希望将 htmx-request
类添加到其他元素,可以使用 hx-indicator 属性,并指定一个 CSS 选择器:
<div>
<button hx-get="/click" hx-indicator="#indicator">
Click Me!
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif"/>
</div>
在这里,我们通过 id 显式指定指示器。注意,我们也可以将类放在父 div
上,并获得相同的效果。
你还可以通过使用 hx-disabled-elt 属性,在请求期间向元素添加 disabled
属性。
如果你希望响应加载到发出请求的元素以外的其他元素中,可以使用 hx-target 属性,该属性接受一个 CSS 选择器。回顾我们的实时搜索示例:
<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>
你可以看到,搜索结果将被加载到 div#search-results
中,而不是输入标签中。
hx-target
以及大多数接受 CSS 选择器的属性支持“扩展” CSS 语法:
this
关键字,表示带有 hx-target
属性的元素本身是目标元素closest <CSS 选择器>
语法将查找匹配给定 CSS 选择器的最近祖先元素或自身。
(例如 closest tr
将定位到该元素最近的表格行)next <CSS 选择器>
语法将查找 DOM 中下一个匹配给定 CSS 选择器的元素。previous <CSS 选择器>
语法将查找 DOM 中上一个匹配给定 CSS 选择器的元素。find <CSS 选择器>
将查找第一个匹配给定 CSS 选择器的子后代元素。
(例如 find tr
将定位到该元素的第一个子后代行)此外,CSS 选择器可以用 <
和 />
字符包裹,模拟 hyperscript 的 查询字面量 语法。
这样的相对目标选择器在创建灵活的用户界面时非常有用,而无需在 DOM 中添加大量 id
属性。
htmx 提供了几种将返回的 HTML 替换到 DOM 中的方式。默认情况下,内容会替换目标元素的 innerHTML
。你可以使用 hx-swap 属性,并设置以下任意值来修改这种行为:
名称 | 描述 |
---|---|
innerHTML | 默认值,将内容放入目标元素内部 |
outerHTML | 用返回的内容替换整个目标元素 |
afterbegin | 在目标内部的第一个子元素之前添加内容 |
beforebegin | 在目标的父元素中,目标元素之前添加内容 |
beforeend | 在目标内部的最后一个子元素之后添加内容 |
afterend | 在目标的父元素中,目标元素之后添加内容 |
delete | 无论响应如何,删除目标元素 |
none | 不从响应中添加内容(带外替换 和 响应头 仍然会被处理) |
除了上述的标准替换机制,htmx 还支持通过扩展进行 变形 替换。变形替换尝试将新内容 合并 到现有的 DOM 中,而不是简单地替换它。这种方法通常可以更好地保留焦点、视频状态等内容,因为它在替换操作期间原地改变现有的节点,代价是更多的 CPU 资源。
以下扩展支持变形替换:
新的、实验性的 视图过渡 API 为开发者提供了一种在不同 DOM 状态之间创建动画过渡的方法。它仍在积极开发中,并未在所有浏览器中可用,但 htmx 提供了一种与该新 API 配合使用的方法,如果在某个浏览器中该 API 不可用,则回退到非过渡机制。
你可以通过以下方法尝试这个新 API:
htmx.config.globalViewTransitions
配置变量为 true
,以对所有替换使用过渡hx-swap
属性中使用 transition:true
选项htmx:beforeTransition
事件,并调用 preventDefault()
来取消过渡。视图过渡可以使用 CSS 进行配置,详细信息请参阅 Chrome 文档中的功能介绍。
你可以在 动画示例 页面上看到视图过渡的示例。
hx-swap 属性支持许多选项,用于调节 htmx 的替换行为。例如,默认情况下,htmx 会替换新内容中的任何标题标签(title 标签)。你可以通过设置 ignoreTitle
修饰符为 true 来关闭此行为:
<button hx-post="/like" hx-swap="outerHTML ignoreTitle:true">Like</button>
hx-swap
上可用的修饰符有:
选项 | 描述 |
---|---|
transition | true 或 false ,是否为此替换使用视图过渡 API |
swap | 在旧内容被清除与新内容插入之间使用的替换延迟(例如 100ms ) |
settle | 在新内容插入与其稳定之间使用的安置延迟(例如 100ms ) |
ignoreTitle | 如果设置为 true ,将忽略新内容中的任何标题,不更新文档标题 |
scroll | top 或 bottom ,将目标元素滚动到其顶部或底部 |
show | top 或 bottom ,将目标元素的顶部或底部滚动到视图中 |
所有替换修饰符出现在替换样式指定之后,并用冒号分隔。
有关这些选项的更多详细信息,请参阅 hx-swap 文档。
通常,你可能希望协调两个元素之间的请求。例如,你可能希望一个元素的请求优先于另一个元素的请求,或者等待另一个元素的请求完成。
htmx 提供了 hx-sync
属性来帮助你实现这一目标。
考虑在此 HTML 中表单提交与单个输入验证请求之间的竞争条件:
<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
,填写输入并立即提交表单会触发 /validate
和 /store
的两个并行请求。
在输入上使用 hx-sync="closest form:abort"
将监视表单上的请求,如果表单请求存在或在输入请求进行中时开始,将中止输入的请求:
<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>
这以声明的方式解决了两个元素之间的同步问题。
htmx 还支持一种编程方式来取消请求:你可以向某个元素发送 htmx:abort
事件来取消任何正在进行的请求:
<button id="request-button" hx-post="/example">
Issue Request
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Cancel Request
</button>
更多示例和详细信息可以在 hx-sync
属性页面 中找到。
htmx 使得在不使用 JavaScript 的情况下使用 CSS 过渡 变得容易。考虑以下 HTML 内容:
<div id="div1">Original Content</div>
假设此内容通过 htmx 通过 ajax 请求替换为以下新内容:
<div id="div1" class="red">New Content</div>
注意两点:
red
类已添加到新内容中在这种情况下,我们可以编写从旧状态到新状态的 CSS 过渡:
.red {
color: red;
transition: all ease-in 1s ;
}
当 htmx 替换新内容时,它会以适用 CSS 过渡的方式进行替换,从而使你获得一个平滑的状态过渡。
总之,要在元素中使用 CSS 过渡,只需确保其 id
在请求之间保持稳定即可!
你可以查看 动画示例 以获取更多详细信息和现场演示。
要理解 CSS 过渡在 htmx 中的实际工作原理,你必须理解 htmx 使用的底层替换和安置模型。
当从服务器接收到新内容时,在替换内容之前,页面的现有内容会被检查是否有与 id
属性匹配的元素。如果在新内容中找到匹配项,旧内容的属性会在替换发生前复制到新元素上。然后新内容会被替换进来,但具有 旧 的属性值。最后,在“安置”延迟(默认情况下为 20ms)之后,新属性值会被替换进来。听起来有点疯狂,但正是这种机制使得开发者在无需任何 JavaScript 的情况下实现了 CSS 过渡。
如果你想通过使用 id
属性直接将响应内容替换到 DOM 中,你可以在 响应 HTML 中使用 [hx-swap-oob](@/attributes/hx-swap-oob
.md) 属性:
<div id="message" hx-swap-oob="true">Swap me directly!</div>
Additional Content
在此响应中,div#message
将被直接替换到匹配的 DOM 元素中,而附加内容将以正常方式替换到目标中。
你可以使用这种技术来“搭载”其他请求的更新。
表格元素与带外替换结合使用时可能会出现问题,因为根据 HTML 规范,许多元素(如 <tr>
或 <td>
)不能单独存在于 DOM 中。
为避免此问题,可以使用 template
标签来封装这些元素:
<template>
<tr id="message" hx-swap-oob="true"><td>Joe</td><td>Smith</td></tr>
</template>
如果你想选择响应 HTML 的一个子集替换到目标中,可以使用 hx-select 属性,该属性接受一个 CSS 选择器并选择响应中匹配的元素。
你也可以使用 hx-select-oob 属性,通过元素 ID 列表选择并替换带外交换的内容。
如果有一些内容你希望在替换过程中保留(例如你希望在替换发生时保持播放状态的视频播放器),你可以在希望保留的元素上使用 hx-preserve 属性。
默认情况下,触发请求的元素如果有值,会自动包含其值。如果该元素是表单,它将包含所有输入项的值。
与 HTML 表单类似,输入项的 name
属性在 htmx 发送的请求中用作参数名。
此外,如果该元素触发的是非 GET
请求,最近的包含表单内的所有输入项的值也将被包含在请求中。
如果你希望包含其他元素的值,可以使用 hx-include 属性,配合一个 CSS 选择器,选择所有你希望在请求中包含其值的元素。
如果你想过滤掉某些参数,可以使用 hx-params 属性。
最后,如果你想以编程方式修改参数,可以使用 htmx:configRequest 事件。
如果你希望通过 htmx 请求上传文件,可以将 hx-encoding 属性设置为 multipart/form-data
。这将使用 FormData
对象提交请求,并将文件正确包含在请求中。
请注意,根据你使用的服务器端技术,你可能需要以非常不同的方式处理这种类型的请求体内容。
注意,htmx 在上传过程中基于标准的 progress
事件定期触发 htmx:xhr:progress
事件,你可以利用此事件显示上传进度。
请参阅 示例部分,了解更高级的表单模式,包括 进度条 和 错误处理。
你可以使用 hx-vals(JSON 格式的名称-表达式对)和 hx-vars 属性(动态计算的以逗号分隔的名称-表达式对)在请求中包含额外的值。
通常,在发出请求之前,你可能需要确认某个操作。htmx 支持 hx-confirm
属性,它允许你使用一个简单的 JavaScript 对话框来确认操作:
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
通过事件,你可以实现更复杂的确认对话框。请参阅 确认示例,了解如何使用 sweetalert2 库来确认 htmx 操作。
另一种进行确认的选项是通过 htmx:confirm
事件 事件。此事件在 每次 触发请求时都会触发(不仅仅是在具有 hx-confirm
属性的元素上),并可用于实现异步确认请求。
以下是一个使用 sweet alert 的示例,应用于具有 confirm-with-sweet-alert='true'
属性的任意元素上:
document.body.addEventListener('htmx:confirm', function(evt) {
if (evt.target.matches("[confirm-with-sweet-alert='true']")) {
evt.preventDefault();
swal({
title: "Are you sure?",
text: "Are you sure you are sure?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
evt.detail.issueRequest();
}
});
}
});
htmx 中的大多数属性是可继承的:它们适用于它们所在的元素以及任何子元素。这使得你可以将属性“提升”到 DOM 上级元素,以避免代码重复。请看以下 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
属性。我们可以将此属性提升到一个父元素上:
<div hx-confirm="Are you sure?">
<button hx-delete="/account">
Delete My Account
</button>
<button hx-put="/account">
Update My Account
</button>
</div>
现在,这个 hx-confirm
属性将应用于其中所有使用 htmx 的元素。
有时你可能希望取消这种继承。例如,如果我们在这组按钮中添加一个取消按钮,但不希望它被确认。我们可以在其上添加一个 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>
顶部的两个按钮将显示确认对话框,但底部的取消按钮不会。
可以使用 hx-disinherit
属性,在每个元素和每个属性的基础上禁用继承。
如果你希望完全禁用属性继承,可以将 htmx.config.disableInheritance
配置变量设置为 true
。这将禁用默认的继承,并允许你通过 hx-inherit
属性明确指定继承。
htmx 支持通过 hx-boost 属性对常规 HTML 锚点和表单进行“增强”。此属性将把所有锚点标签和表单转换为 AJAX 请求,并默认将响应目标设置为页面的主体部分。
这是一个示例:
<div hx-boost="true">
<a href="/blog">Blog</a>
</div>
此 div 中的锚点标签将发出一个 AJAX GET
请求到 /blog
,并将响应内容替换到 body
标签内。
hx-boost
的一个特点是,如果没有启用 JavaScript,它会优雅地降级:链接和表单继续工作,只是它们不再使用 AJAX 请求。这就是所谓的 渐进增强,它允许更广泛的受众使用你网站的功能。
其他 htmx 模式也可以调整以实现渐进增强,但这需要更多的思考。
以 主动搜索 示例为例。按目前的写法,它不会优雅地降级:那些没有启用 JavaScript 的用户将无法使用此功能。这样做是为了简化示例,使其尽可能简洁。
然而,你可以将 htmx 增强的输入元素包装在一个表单元素中:
<form action="/search" method="POST">
<input class="form-control" type="search"
name="search" placeholder="Begin typing to search users..."
hx-post="/search"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
</form>
这样一来,启用 JavaScript 的客户端仍然会获得良好的主动搜索用户体验,而未启用 JavaScript 的客户端可以通过按回车键进行搜索。更好的是,你还可以添加一个“搜索”按钮。然后你需要在表单上更新一个 hx-post
,使其与 action
属性一致,或者在表单上使用 hx-boost
。
你需要在服务器端检查 HX-Request
头,以区分 htmx 驱动的请求和常规请求,从而确定应向客户端呈现什么内容。
其他模式也可以类似调整,以满足你的应用程序的渐进增强需求。
如你所见,这需要更多的思考和工作。这也完全排除了某些功能。这些权衡必须由你,即开发人员,根据你的项目目标和受众做出。
无障碍性 是一个与渐进增强密切相关的概念。使用像 hx-boost
这样的渐进增强技术,将使你的 htmx 应用程序对更广泛的用户更具无障碍性。
基于 htmx 的应用程序与普通的非 AJAX 驱动的 Web 应用程序非常相似,因为 htmx 是面向 HTML 的。
因此,常规的 HTML 无障碍性建议同样适用于 htmx 应用程序。例如:
通过扩展可以支持 WebSockets 和服务器发送事件(SSE)。请参阅 SSE 扩展 和 WebSocket 扩展 页面了解更多信息。
Htmx 提供了一种简单的机制与 浏览器历史记录 API 进行交互:
如果你希望某个元素将其请求的 URL 推送到浏览器的导航栏,并将当前页面的状态添加到浏览器的历史记录中,可以包含 hx-push-url 属性:
<a hx-get="/blog" hx-push-url="true">博客</a>
当用户点击这个链接时,htmx 将会在发出对 /blog 的请求之前,快照当前的 DOM 并存储它。然后,它会执行内容替换并将新的位置推送到历史堆栈中。
当用户点击后退按钮时,htmx 会从存储中检索旧的内容,并将其替换回目标区域,模拟“返回”到之前的状态。如果在缓存中找不到该位置,htmx 将向给定的 URL 发出一个带有 HX-History-Restore-Request
标头的 Ajax 请求,并期望返回整个页面所需的 HTML。或者,如果 htmx.config.refreshOnHistoryMiss
配置变量设置为 true,它将进行硬刷新。
注意: 如果你将 URL 推送到历史记录中,你 必须 能够导航到该 URL 并获取完整的页面!用户可能会将 URL 复制并粘贴到电子邮件或新标签页中。此外,如果页面不在历史缓存中,htmx 在恢复历史记录时将需要整个页面。
默认情况下,htmx 将使用 body
来进行历史快照的获取和恢复。这通常是正确的做法,但如果你想使用更小范围的元素进行快照,可以使用 hx-history-elt 属性指定一个不同的元素。
注意:这个元素需要在所有页面上都存在,否则从历史记录恢复时将无法正常工作。
如果你使用了第三方库,并希望使用 htmx 的历史记录功能,你需要在快照之前清理 DOM。让我们考虑 Tom Select 库,它使 select 元素的用户体验更加丰富。我们来设置 TomSelect 将带有 .tomselect
类的任何输入元素变成一个丰富的选择元素。
首先,我们需要初始化新内容中带有该类的元素:
htmx.onLoad(function (target) {
// 找到新内容中所有应当
// 作为编辑器的元素,并用 TomSelect 初始化
var editors = target.querySelectorAll(".tomselect")
.forEach(elt => new TomSelect(elt))
});
这将为所有带有 .tomselect
类的输入元素创建一个丰富的选择器。然而,它会修改 DOM,我们不希望这种修改被保存到历史缓存中,因为当历史内容重新加载到屏幕时,TomSelect 会被重新初始化。
为了解决这个问题,我们需要捕获 htmx:beforeHistorySave
事件,并通过调用 destroy()
来清理 TomSelect 的变更:
htmx.on('htmx:beforeHistorySave', function() {
// 找到所有 TomSelect 元素
document.querySelectorAll('.tomSelect')
.forEach(elt => elt.tomselect.destroy()) // 并调用 destroy() 方法
})
这将 DOM 恢复到原始的 HTML,从而允许进行干净的快照。
可以通过在当前文档中的任何元素或通过 htmx 加载到当前文档中的任何 HTML 片段上设置 hx-history 属性为 false
来禁用某个 URL 的历史快照。此方法可以防止敏感数据进入 localStorage 缓存,这对于共享使用/公共计算机可能很重要。历史导航将正常工作,但在恢复时,URL 将从服务器请求,而不是从本地历史缓存中请求。
Htmx 期望其发出的 AJAX 请求的响应为 HTML,通常是 HTML 片段(虽然配合 hx-select 标签使用时,完整的 HTML 文档也很有用)。Htmx 会将返回的 HTML 替换到指定的目标位置,并使用指定的替换策略。
有时,你可能希望在替换时不做任何操作,但仍可能触发客户端事件(见下文)。
对于这种情况,默认情况下,你可以返回 204 - No Content
响应码,htmx 将忽略响应内容。
如果服务器返回错误响应(例如 404 或 501),htmx 将触发 htmx:responseError
事件,你可以处理该事件。
在发生连接错误时,将触发 htmx:sendError
事件。
你可以通过修改或替换 htmx.config.responseHandling
数组来配置上述 htmx 的行为。该对象是由 JavaScript 对象组成的集合,定义如下:
responseHandling: [
{code:"204", swap: false}, // 204 - No Content 默认不执行任何操作,但不视为错误
{code:"[23]..", swap: true}, // 200 & 300 响应码视为非错误并执行替换
{code:"[45]..", swap: false, error:true}, // 400 & 500 响应码不执行替换并视为错误
{code:"...", swap: false} // 捕获所有其他响应码
]
当 htmx 接收到响应时,它将按顺序遍历 htmx.config.responseHandling
数组,并测试其中对象的 code
属性(当其作为正则表达式处理时)是否与当前响应匹配。如果某个条目与当前响应码匹配,它将用于确定如何处理响应。
此数组中的条目可用于响应处理配置的字段包括:
code
- 一个表示将用于测试响应码的正则表达式的字符串。swap
- 如果应将响应内容替换到 DOM 中,则为 true
,否则为 false
error
- 如果 htmx 应将此响应视为错误,则为 true
ignoreTitle
- 如果 htmx 应忽略响应中的标题标签,则为 true
select
- 用于从响应中选择内容的 CSS 选择器target
- 指定响应替换的目标的 CSS 选择器swapOverride
- 替换响应内容的替代机制作为使用此配置的示例,考虑一个服务器端框架在验证错误发生时返回 422 - Unprocessable Entity
响应的情况。默认情况下,htmx 将忽略响应,因为它与正则表达式 [45]..
匹配。
使用 meta 配置 机制来配置 responseHandling,我们可以添加如下配置:
<!--
* 204 No Content 默认不执行任何操作,但不视为错误
* 2xx, 3xx 和 422 响应码视为非错误并执行替换
* 4xx & 5xx 响应码不执行替换并视为错误
* 所有其他响应码使用 "..." 作为捕获全部的替换方式
-->
<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}
]
}'
/>
如果你希望无论 HTTP 响应码如何,所有内容都进行替换,可以使用以下配置:
<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' /> <!--所有响应均进行替换-->
最后,值得考虑使用 响应目标 扩展,该扩展允许你通过属性声明式地配置响应码的行为。
在跨域上下文中使用 htmx 时,请记得配置您的 Web 服务器,以设置 Access-Control 标头,以便 htmx 标头在客户端可见。
htmx 在请求中包含许多有用的标头:
标头 | 描述 |
---|---|
HX-Boosted | 表示该请求是通过使用 hx-boost 的元素发起的 |
HX-Current-URL | 浏览器的当前 URL |
HX-History-Restore-Request | 如果请求是为本地历史记录缓存未命中的历史恢复,则为 “true” |
HX-Prompt | 用户对 hx-prompt 的响应 |
HX-Request | 总是为 “true” |
HX-Target | 如果存在,则为目标元素的 id |
HX-Trigger-Name | 如果存在,则为触发元素的 name |
HX-Trigger | 如果存在,则为触发元素的 id |
htmx 支持一些特定于 htmx 的响应标头:
HX-Location
- 允许您执行不刷新整个页面的客户端重定向HX-Push-Url
- 将新 URL 推送到历史堆栈中HX-Redirect
- 可用于客户端重定向到新位置HX-Refresh
- 如果设置为 “true”,客户端将刷新整个页面HX-Replace-Url
- 替换地址栏中的当前 URLHX-Reswap
- 允许您指定响应如何交换。请参阅 hx-swap 获取可能的值HX-Retarget
- 一个 CSS 选择器,用于将内容更新的目标更改为页面上的另一个元素HX-Reselect
- 一个 CSS 选择器,允许您选择响应中要交换的部分内容。覆盖触发元素上的现有 hx-select
HX-Trigger
- 允许您触发客户端事件HX-Trigger-After-Settle
- 允许您在定居步骤后触发客户端事件HX-Trigger-After-Swap
- 允许您在交换步骤后触发客户端事件有关 HX-Trigger
标头的更多信息,请参阅 HX-Trigger
响应标头。
通过 htmx 提交表单的好处在于不再需要 Post/Redirect/Get 模式。在服务器成功处理 POST 请求后,您不需要返回 HTTP 302 (重定向)。您可以直接返回新的 HTML 片段。
htmx 请求的操作顺序如下:
htmx-request
类被应用到相关元素上htmx-swapping
类htmx-swapping
类htmx-added
类添加到每个新内容中htmx-settling
类应用到目标中htmx-settling
类htmx-added
类您可以使用 htmx-swapping
和 htmx-settling
类在页面之间创建 CSS 过渡。
htmx 与 HTML5 验证 API 集成,如果可验证的输入无效,则不会发出表单请求。这适用于 AJAX 请求和 WebSocket 发送。
htmx 在验证期间触发一些事件,可以用于挂钩自定义验证和错误处理:
htmx:validation:validate
- 在调用元素的 checkValidity()
方法之前调用。可用于添加自定义验证逻辑htmx:validation:failed
- 当 checkValidity()
返回 false 时调用,表示输入无效htmx:validation:halted
- 由于验证错误未发出请求时调用。具体错误可以在 event.detail.errors
对象中找到默认情况下,非表单元素在发出请求之前不会进行验证,但您可以通过将 hx-validate
属性设置为 “true” 来启用验证。
以下是一个输入框的示例,它使用 hx-on
属性捕捉 htmx:validation:validate
事件并要求输入值为 foo
:
<form id="example-form" hx-post="/test">
<input name="example"
onkeyup="this.setCustomValidity('') // 在按键时重置验证"
hx-on:htmx:validation:validate="if(this.value != 'foo') {
this.setCustomValidity('请输入值 foo') // 设置验证错误
htmx.find('#example-form').reportValidity() // 报告问题
}">
</form>
请注意,所有客户端验证都必须在服务器端重新进行,因为它们始终可以被绕过。
htmx 允许您仅使用 HTML 和 CSS 在许多情况下使用 CSS 过渡。
有关可用选项的更多详细信息,请参阅 动画指南。
htmx 具有一个扩展机制,允许您自定义库的行为。扩展 在 JavaScript 中定义,然后通过 hx-ext
属性使用:
<div hx-ext="debug">
<button hx-post="/example">此按钮使用了 debug 扩展</button>
<button hx-post="/example" hx-ext="ignore:debug">此按钮未使用</button>
</div>
如果您有兴趣向 htmx 添加自己的扩展,请 参阅扩展文档
htmx 拥有广泛的 事件机制,该机制也是日志系统。
如果您想注册某个 htmx 事件,您可以使用
document.body.addEventListener('htmx:load', function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
或者,如果您愿意,可以使用以下 htmx 辅助工具:
htmx.on("htmx:load", function(evt) {
myJavascriptLib.init(evt.detail.elt);
});
每当 htmx 将元素加载到 DOM 中时,都会触发 htmx:load
事件,这实际上相当于正常的 load
事件。
一些 htmx 事件的常见用途包括:
使用 htmx:load
事件来初始化内容是如此常见,以至于 htmx 提供了一个辅助函数:
htmx.onLoad(function(target) {
myJavascriptLib.init(target);
});
这与第一个示例做的事情相同,但稍微简洁一些。
您可以处理 htmx:configRequest
事件,以便在发出 AJAX 请求之前修改它:
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.parameters['auth_token'] = getAuthToken(); // 在请求中
添加新参数
evt.detail.headers['Authentication-Token'] = getAuthToken(); // 在请求中添加新标头
});
在这里,我们在请求发送之前添加了一个参数和标头。
您可以处理 htmx:beforeSwap
事件,以便修改 htmx 的交换行为:
document.body.addEventListener('htmx:beforeSwap', function(evt) {
if(evt.detail.xhr.status === 404){
// 当出现 404 错误时提醒用户(可以使用比 alert 更好的机制)
alert("错误:无法找到资源");
} else if(evt.detail.xhr.status === 422){
// 允许 422 响应进行交换,因为我们使用它作为信号,表明
// 表单提交了错误的数据,并希望重新渲染错误信息
//
// 设置 isError 为 false,以避免在控制台中记录错误
evt.detail.shouldSwap = true;
evt.detail.isError = false;
} else if(evt.detail.xhr.status === 418){
// 如果返回代码 418(我是一个茶壶),将响应内容的目标重新定位到 id 为 `teapot` 的元素
evt.detail.shouldSwap = true;
evt.detail.target = htmx.find("#teapot");
}
});
在这里,我们处理了一些 400级错误响应代码,这些代码通常不会在 htmx 中执行交换。
请注意,所有事件都以两种不同的名称触发:
因此,例如,您可以监听 htmx:afterSwap
或 htmx:after-swap
。这有助于与其他库的互操作性。例如,Alpine.js 需要短横线命名法。
如果您在 htmx.logger
中设置了一个记录器,那么每个事件都会被记录下来。这对于故障排除非常有用:
htmx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
使用 htmx(或任何其他声明式语言)进行声明式和事件驱动编程可能是一项令人愉快且富有成效的活动,但与命令式方法相比,它的一个缺点是调试可能更棘手。
例如,找出为什么某些事情没有发生,可能很困难,如果您不知道技巧的话。
好吧,这些就是技巧:
您可以使用的第一个调试工具是 htmx.logAll()
方法。这将记录 htmx 触发的每个事件,并允许您准确地查看库的操作。
htmx.logAll();
当然,这不能告诉您 htmx 没有做某事的原因。您可能还不知道用作触发器的 DOM 元素触发了什么事件。为了解决这个问题,您可以使用浏览器控制台中可用的 monitorEvents()
方法:
monitorEvents(htmx.find("#theElement"));
这将向控制台输出 id 为 theElement
的元素上发生的所有事件,并允许您准确查看它的操作情况。
请注意,这仅在控制台中有效,您无法将其嵌入页面的脚本标签中。
最后,推到最后一步,您可能只想通过加载未压缩的版本来调试 htmx.js
。它大约有 2500 行 JavaScript,因此并不是无法应付的代码量。您最有可能想要在 issueAjaxRequest()
和 handleAjaxResponse()
方法中设置断点,以查看发生了什么。
随时可以加入 Discord 寻求帮助。
有时,为了演示一个错误或澄清用法,使用类似 jsfiddle 的 JavaScript 片段网站是很好的选择。为了便于轻松创建演示,htmx 提供了一个演示脚本网站,该网站将安装:
只需将以下脚本标签添加到您的演示/小提琴/其他内容中:
<script src="https://demo.htmx.org"></script>
此助手允许您通过添加 template
标签并使用 url
属性指示哪个 URL 来添加模拟响应。该 URL 的响应将是 template 的 innerHTML,从而使构建模拟响应变得容易。您可以使用 delay
属性为响应添加延迟,该属性应为一个整数,表示延迟的毫秒数。
您可以使用 ${}
语法在模板中嵌入简单表达式。
请注意,此功能仅用于演示,并且无法保证长期有效,因为它将始终获取 htmx 和 hyperscript 的最新版本!
以下是代码的实际示例:
<!-- 加载演示环境 -->
<script src="https://demo.htmx.org"></script>
<!-- post 到 /foo -->
<button hx-post="/foo" hx-target="#result">
计数增加
</button>
<output id="result"></output>
<!-- 使用模板标签中的一些动态内容响应 /foo -->
<script>
globalInt = 0;
</script>
<template url="/foo" delay="500"> <!-- 注意 url 和 delay 属性 -->
${globalInt++}
</template>
虽然 htmx 鼓励使用超媒体方法构建 Web 应用程序,但它为客户端脚本提供了许多选项。脚本编写包括在 Web 架构的 REST-ful 描述中,参见:按需编码。尽可能多地推荐在 Web 应用程序中采用 超媒体友好 的脚本编写方法:
htmx 与脚本解决方案之间的主要集成点是 htmx 发送并响应的事件。请参阅 第三方 JavaScript 部分中的 SortableJS 示例,以获取通过事件与 htmx 集成 JavaScript 库的良好模板。
与 htmx 配合良好的脚本解决方案包括:
我们在 我们的书 中有一整章标题为 “客户端脚本” 的章节,介绍了如何将脚本编写集成到基于 htmx 的应用程序中。
hx-on*
属性HTML 允许通过 onevent
属性 嵌入内联脚本,例如 onClick
:
<button onclick="alert('你点击了我!')">
点击我!
</button>
此功能允许将脚本逻辑与其适用的 HTML 元素一起定位,从而提供良好的 行为局部性 (LoB)。不幸的是,HTML 仅允许
on*
属性用于固定数量的 特定 DOM 事件(例如 onclick
),并且不提供响应元素上的任意事件的通用机制。
为了解决这个问题,htmx 提供了 hx-on*
属性。这些属性允许您以保留标准 on*
属性的 LoB 的方式响应任何事件。
如果我们想使用 hx-on
属性响应 click
事件,我们会这样写:
<button hx-on:click="alert('你点击了我!')">
点击我!
</button>
因此,字符串 hx-on
,后跟冒号(或短横线),然后是事件名称。
当然,对于 click
事件,我们建议坚持使用标准的 onclick
属性。然而,考虑到一个 htmx 驱动的按钮希望使用 htmx:config-request
事件来添加参数。使用标准的 on*
属性无法实现此目的,但可以使用 hx-on:htmx:config-request
属性实现:
<button hx-post="/example"
hx-on:htmx:config-request="event.detail.parameters.example = 'Hello Scripting!'">
提交我!
</button>
在这里,在发出 POST
请求之前添加了 example
参数,值为 ‘Hello Scripting!’。
hx-on*
属性是一种用于通用嵌入式脚本的非常简单的机制。它不是更完善的前端脚本解决方案(如 AlpineJS 或 hyperscript)的替代品。然而,它可以增强基于 VanillaJS 的脚本编写方法在 htmx 驱动的应用程序中的应用。
请注意,HTML 属性是不区分大小写的。这意味着,不幸的是,依赖于大写/驼峰式命名法的事件无法响应。如果您需要支持驼峰式命名事件,我们建议使用更完善的脚本解决方案,如 AlpineJS 或 hyperscript。htmx 为此原因同时触发驼峰式命名法和短横线命名法的事件。
htmx 与第三方库集成得相当好。如果库在 DOM 上触发事件,您可以使用这些事件来触发 htmx 的请求。
SortableJS 示例 是一个很好的示例:
<form class="sortable" hx-post="/items" hx-trigger="end">
<div class="htmx-indicator">正在更新...</div>
<div><input type='hidden' name='item' value='1'/>项 1</div>
<div><input type='hidden' name='item' value='2'/>项 2</div>
<div><input type='hidden' name='item' value='2'/>项 3</div>
</form>
与 Sortable 类似,大多数 JavaScript 库需要在某个时刻初始化内容。
在 jQuery 中,您可能会这样做:
$(document).ready(function() {
var sortables = document.body.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
});
在 htmx 中,您将改用 htmx.onLoad
函数,并且仅从新加载的内容中选择,而不是整个文档:
htmx.onLoad(function(content) {
var sortables = content.querySelectorAll(".sortable");
for (var i = 0; i < sortables.length; i++) {
var sortable = sortables[i];
new Sortable(sortable, {
animation: 150,
ghostClass: 'blue-background-class'
});
}
})
这将确保在 htmx 向 DOM 中添加新内容时,排序元素被正确初始化。
如果 JavaScript 将内容添加到 DOM 中并且该内容包含 htmx 属性,则需要确保使用 htmx.process()
函数初始化该内容。
例如,如果您使用 fetch
API 获取一些数据并将其放入一个 div 中,并且该 HTML 中包含 htmx 属性,您需要添加对 htmx.process()
的调用,如下所示:
let myDiv = document.getElementById('my-div')
fetch('http://example.com/movies.json')
.then(response => response.text())
.then(data => { myDiv.innerHTML = data; htmx.process(myDiv); } );
一些第三方库从 HTML 模板元素创建内容。例如,Alpine JS 使用 x-if
属性在模板上有条件地添加内容。这些模板最初不属于 DOM,如果它们包含 htmx 属性,则在加载后需要调用 htmx.process()
。以下示例使用 Alpine 的 $watch
函数来监听值的变化,从而触发条件内容:
<div x-data="{show_new: false}"
x-init="$watch('show_new', value => {
if (show_new) {
htmx.process(document.querySelector('#new_content'))
}
})">
<button @click = "show_new = !show_new">切换新内容</button>
<template x-if="show_new">
<div id="new_content">
<a hx-get="/server/newstuff" href="#">新的可点击内容</a>
</div>
</template>
</div>
请参阅 Web 组件示例 页面,了解如何将 htmx 与 Web 组件集成。
htmx 开箱即用地支持标准 HTTP 缓存 机制。
如果您的服务器向特定 URL 的响应添加了 Last-Modified
HTTP 响应标头,浏览器将自动向下次对相同 URL 的请求添加 If-Modified-Since
请求 HTTP 标头。请注意,如果您的服务器可以根据某些其他标头为同一 URL 渲染不同的内容,您需要使用 Vary
响应 HTTP 标头。例如,如果您的服务器在 HX-Request
标头丢失或为 false
时渲染完整 HTML,而在 HX-Request: true
时渲染该 HTML 的片段,您需要添加 Vary: HX-Request
。这会导致缓存基于响应 URL 和 HX-Request
请求标头的组合进行密钥分配,而不是仅基于响应 URL。
如果您无法(或不愿意)使用 Vary
标头,您可以选择将配置参数 getCacheBusterParam
设置为 true
。如果设置了此配置变量,htmx 将在它发出的 GET
请求中包含一个缓存破坏参数,这将防止浏览器在相同的缓存槽中缓存基于 htmx 的响应和非 htmx 的响应。
htmx 还与 ETag
正常工作。请注意,如果您的服务器可以为同一 URL 渲染不同的内容(例如,取决于 HX-Request
标头的值),则服务器需要为每个内容生成不同的 ETag
。
htmx 允许您直接在 DOM 中定义逻辑。这具有许多优点,最大的是 行为局部性,使您的系统更易于理解和维护。
然而,这种方法的一个担忧是安全性:由于 htmx 增强了 HTML 的表达能力,如果恶意用户能够向您的应用程序注入 HTML,他们可以利用 htmx 的这种表达能力来达到恶意目的。
HTML 的 Web 开发的第一条规则始终是:不要信任用户的输入。您应该转义注入到您网站中的所有第三方、不受信任的内容。这是为了防止,包括在内的,跨站脚本攻击 (XSS)。
在优秀的 OWASP 网站 上有关于 XSS 及其防止的广泛文档,包括 跨站脚本防护备忘单。
好消息是,这是一个非常古老且广为人知的话题,大多数服务器端模板语言都支持 自动转义 内容以防止此类问题。
即便如此,有时人们选择以更危险的方式注入 HTML,通常通过某种
raw()
机制在他们的模板语言中。这样做可能是出于好的原因,但如果注入的内容来自第三方,那么它必须经过清理,包括删除以 hx-
和 data-hx
开头的属性,以及内联 <script>
标签等。
如果您正在注入原始 HTML 并自行处理转义,最佳做法是白名单您允许的属性和标签,而不是列出您不允许的属性和标签。
当然,错误总是会发生,开发人员也不是完美的,因此对您的 Web 应用程序进行分层安全是好的做法,htmx 还提供了帮助保护应用程序的工具。
让我们来看看它们。
hx-disable
htmx 提供的第一个安全工具是 hx-disable
属性。此属性将阻止处理给定元素上的所有 htmx 属性以及其内的所有元素。因此,例如,如果您在模板中包含原始 HTML 内容(再次,不推荐!),则可以在内容周围放置一个带有 hx-disable
属性的 div:
<div hx-disable>
<%= raw(user_content) %>
</div>
htmx 将不会处理该内容中发现的任何 htmx 相关属性或功能。无法通过注入进一步的内容来禁用此属性:如果在元素的父层级中发现 hx-disable
属性,htmx 将不会处理它。
hx-history
另一个安全考虑因素是 htmx 历史缓存。您可能有一些页面包含敏感数据,不希望存储在用户的 localStorage
缓存中。您可以通过在页面上的任何位置包含 hx-history
属性,并将其值设置为 false
,来省略历史缓存中的某个页面。
htmx 还提供了与安全相关的配置选项:
htmx.config.selfRequestsOnly
- 如果设置为 true
,只允许对与当前文档相同域的请求htmx.config.allowScriptTags
- htmx 将处理其加载的新内容中发现的 <script>
标签。如果您希望禁用此行为,可以将此配置变量设置为 false
htmx.config.historyCacheSize
- 可以设置为 0
,以避免在 localStorage
缓存中存储任何 HTMLhtmx.config.allowEval
- 可以设置为 false
,以禁用 htmx 依赖 eval 的所有功能:
hx-on:
属性js:
前缀的 hx-vals
js:
前缀的 hx-headers
请注意,禁用 eval()
移除的所有功能都可以通过自定义 JavaScript 和 htmx 事件模型重新实现。
如果您希望允许请求访问除当前主机之外的某些域,但不想完全开放,可以使用 htmx:validateUrl
事件。此事件将在 detail.url
插槽中提供请求 URL,并具有 sameHost
属性。
您可以检查这些值,如果请求无效,可以在事件上调用 preventDefault()
来阻止请求的发出。
document.body.addEventListener('htmx:validateUrl', function (evt) {
// 只允许请求当前服务器以及 myserver.com
if (!evt.detail.sameHost && evt.detail.url.hostname !== "myserver.com") {
evt.preventDefault();
}
});
浏览器还提供了进一步保护您的 Web 应用程序的工具。最强大的工具是 内容安全策略 (Content Security Policy, CSP)。使用 CSP,您可以指示浏览器,例如,不要向非来源主机发出请求,不要评估内联脚本标签等。
以下是 meta
标签中的 CSP 示例:
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
这告诉浏览器“只允许连接到原始(来源)域”。这与 htmx.config.selfRequestsOnly
是冗余的,但在处理应用程序安全性时,分层安全方法是必要的,并且实际上是理想的。
全面讨论 CSP 超出了本文档的范围,但 MDN 文章 提供了探索此主题的良好起点。
htmx 具有一些可以通过编程或声明方式访问的配置选项。它们列举如下:
配置变量 | 信息 |
---|---|
htmx.config.historyEnabled | 默认为 true ,仅对测试有用 |
htmx.config.historyCacheSize | 默认为 10 |
htmx.config.refreshOnHistoryMiss | 默认为 false ,如果设置为 true ,htmx 将在历史记录丢失时进行完整的页面刷新,而不是使用 AJAX 请求 |
htmx.config.defaultSwapStyle | 默认为 innerHTML |
htmx.config.defaultSwapDelay | 默认为 0 |
htmx.config.defaultSettleDelay | 默认为 20 |
htmx.config.includeIndicatorStyles | 默认为 true (确定是否加载指示器样式) |
htmx.config.indicatorClass | 默认为 htmx-indicator |
htmx.config.requestClass | 默认为 htmx-request |
htmx.config.addedClass | 默认为 htmx-added |
htmx.config.settlingClass | 默认为 htmx-settling |
htmx.config.swappingClass | 默认为 htmx-swapping |
htmx.config.allowEval | 默认为 true ,可以用于禁用 htmx 对 eval 的使用,以禁用某些功能(例如触发器过滤器) |
htmx.config.allowScriptTags | 默认为 true ,决定 htmx 是否会处理新内容中发现的脚本标签 |
htmx.config.inlineScriptNonce | 默认为 '' ,这意味着不会向内联脚本添加 nonce |
htmx.config.attributesToSettle | 默认为 ["class", "style", "width", "height"] ,在定居阶段要稳定的属性 |
htmx.config.inlineStyleNonce | 默认为 '' ,这意味着不会向内联样式添加 nonce |
htmx.config.useTemplateFragments | 默认为 false ,HTML 模板标签用于从服务器解析内容(不兼容 IE11!) |
htmx.config.wsReconnectDelay | 默认为 full-jitter |
htmx.config.wsBinaryType | 默认为 blob ,接收的二进制数据类型 |
htmx.config.disableSelector | 默认为 [hx-disable], [data-hx-disable] ,htmx 将不会处理带有此属性的元素或其父元素 |
htmx.config.withCredentials | 默认为 false ,允许跨站点使用凭据(如 cookies、授权标头或 TLS 客户端证书)进行 Access-Control 请求 |
htmx.config.timeout | 默认为 0,请求在自动终止之前可以进行的毫秒数 |
htmx.config.scrollBehavior | 默认为 ‘smooth’,页面过渡中加速链接的行为。允许的值为 auto 和 smooth 。Smooth 将平滑滚动到页面顶部,而 auto 将表现为普通链接。 |
htmx.config.defaultFocusScroll | 是否应滚动到视图中的聚焦元素,默认为 false,可以使用 focus-scroll 交换修饰符进行覆盖。 |
htmx.config.getCacheBusterParam | 默认为 false,如果设置为 true,htmx 将在 GET 请求中附加目标元素,格式为 org.htmx.cache-buster=targetElementId |
htmx.config.globalViewTransitions | 如果设置为 true ,htmx 将在交换新内容时使用 View Transition API。 |
htmx.config.methodsThatUseUrlParams | 默认为 ["get"] ,htmx 将使用这些方法通过在 URL 中编码其参数而不是请求主体来格式化请求 |
htmx.config.selfRequestsOnly | 默认为 true ,是否仅允许 AJAX 请求到与当前文档相同的域 |
htmx.config.ignoreTitle | 默认为 false ,如果设置为 true ,htmx 将在新内容中找到 title 标签时不会更新文档标题 |
htmx.config.disableInheritance | 禁用 htmx 中的属性继承,然后可以通过 hx-inherit 属性进行覆盖 |
htmx.config.scrollIntoViewOnBoost | 默认为 true ,是否将加速元素的目标滚动到视图中。如果加速元素上省略了 hx-target ,目标默认为 body ,导致页面滚动到顶部。 |
htmx.config.triggerSpecsCache | 默认为 null ,用于存储已评估触发器规格的缓存,提高解析性能,代价是更多的内存使用。您可以定义一个简单的对象来使用永不清除的缓存,或者使用 代理对象 实现自己的系统。 |
htmx.config.allowNestedOobSwaps | 默认为 true ,是否处理嵌套在主响应元素内的 OOB 交换。请参阅 嵌套 OOB 交换。 |
您可以直接在 JavaScript 中设置这些选项,或者使用 meta
标签:
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
这就是全部内容!
享受使用 htmx 的乐趣吧!您可以在不编写大量代码的情况下
完成很多事情!