详解 hx-select 与 OOB 的区别及混合使用时的注意事项

hx-select

《一文读懂 HTMX OOB 带外交换》这篇文章中已经详细介绍了 HTMX OOB(带外交换)的用法。今天这篇文章就来介绍 hx-select 属性的用法,这个用法与 OOB 有着几乎可以说是相反的功能:

  • OOB 是允许从云服务器的一次响应中同时交换到页面的多个元素;
  • hx-select 是允许从云服务器的一次响应中的多个元素中选择性地只交换一个元素的内容;

示例代码

客户端 HTML

<!-- index.htm -->
<div id="target1">target1</div>
<div id="target2">target2</div>
<div id="normal_target">normal_target</div>
<button hx-get="/oob" hx-target="#normal_target">OOB</button>
<button hx-get="/select" hx-target="#normal_target" hx-select="#target1">Select</button>

以上 HTML 代码有两个按钮,第一个是用来对比的 OOB ,第二个是本次重点介绍的 hx-select。

Select 按钮上加入了 hx-select="#target1" 属性,这行代码的意思是从服务端返回的内容中查找 ID 为 target1 的元素,然后把内容交换到 hx-target 指定的 ID 为 normal_target 的元素中,服务端返回的其它内容则会被丢弃。

服务端 Python Flask 代码

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template("index.html")

@app.route('/oob')
def oob():
    return render_template("oob.html")

@app.route('/select')
def select():
    return render_template("select.html")

if __name__ == '__main__':
    app.run(debug=True)

服务端主要是三个路由:

  • 加载主页的根路由
  • 加载 OOB 响应内容的 /oob
  • 加载 hx-select 响应内容的 /select

服务端响应内容

<!-- select.html -->
<div id="target1">From server,will fill in target 1</div>
<div id="target2">From server,will fill in target 2</div>
<div>From server,normal content</div>

服务端响应的内容必须包含客户端 HTML hx-select 指定的元素,否则无法正常交换内容。

因为这次的重点是介绍 hx-select ,为了避免文章篇幅过长,只贴出 select.html 的代码,完整的代码大家可以到 GitHub 上免费获取。

hx-select 交换效果

刷新主页并点击 Select 按钮后,服务端响应内容中 ID 为 target1 的内容被交换到了当前页面 ID 为 normal_target 的元素内部(因为没有显式指定 hx-swap ,所以默认是 innerHTML,相当于 hx-swap="innerHTML"),而服务端响应的其它内容则被丢弃了,没有出现在当前页面的任何地方。

<div id="target1">target1</div>
<div id="target2">target2</div>
<!-- 交换了这一行 -->
<div id="normal_target" class=""><div id="target1">From server,will fill in target 1</div></div>
<button hx-get="/oob" hx-target="#normal_target">OOB</button>
<button hx-get="/select" hx-target="#normal_target" hx-select="#target1" class="">Select</button>

hx-select 应用场景

hx-select 很适合用在服务端无法判断是要返回一个完整的 HTML 页面还是只需要返回部分 HTML 片段的情况下使用,因为在客户端是能够相对清晰地知道当前用户操作的期望是什么的。

比如说当前需要请求一个文章列表,但服务端无法判断是只需要返回文章列表的内容还是需要返回一个包含文章列表内容的完整页面。这时候就可以由服务端全部返回一个完整的页面内容,由客户端来选择展示整个完整页面还是只选取文章列表。

又或者是一个公共功能模块,在某些时候需要以一个完整的单独页面的形式展示,但在某些时候又需要以弹窗的形式展示。弹窗的时候就可以使用 hx-select 取其中关键内容进行展示,这样就不会破坏客户端的页面布局。

hx-select 与 OOB 同时使用

<div id="target1" hx-swap-oob="true">From server,will fill in target 1</div>
<div id="target2">From server,will fill in target 2</div>
<div>From server,normal content</div>

当我们尝试同时使用 hx-select 与 OOB 时,如果 hx-select 与 hx-swap-oob 作用在同一个响应元素中时,OOB 优先级较高,hx-select 将会得到空白的内容。

如以上代码中,为 target1 加入了 hx-swap-oob,加载后客户端 HTML 中的 target1 就会获得响应内容 target1 的内容,而 normal_target 则会变成空。

如果 hx-select 与 hx-swap-oob 指向不同的响应元素时,则两个都会正常工作,不会发生交换冲突。

比如:

<div id="target1">From server,will fill in target 1</div>
<div id="target2" hx-swap-oob="true">From server,will fill in target 2</div>
<div>From server,normal content</div>

注意事项

  • 如何使用了 hx-select 属性,则要保证服务端返回的响应内容中包含 hx-select 指定的元素;
  • hx-select 是从响应中选择要交换的内容,交换的目标与交换方式还是要使用 hx-target 及 hx-swap 指定(可以使用默认值)
  • hx-select 与 OOB 同时指向同一个响应元素时,OOB 的优先级较高,hx-select 将会得到空的内容;

本文上的示例代码已经上传到 GitHub