HTMX 不生效?探索 JavaScript 动态 HTML 加载的终极解决方案

2025-01-05阅读数:144
上一篇: HTMX 与 VUE之间的对比
下一篇: HTMX实现无表单提交的妙招:解锁更简洁高效的交互方式

在使用htmx的过程中,遇到了通过JavaScript动态加载的HTML内容包含的htmx行为不生效的问题。

在把一些老的项目升级替换成htmx时,难免会为了兼容旧代码而使用javascript加载html片段,在这当中遇到了一个奇怪的问题:加载的html片段中包含的hx-get、hx-post等代码不生效,点击时完全没响应。但把这些代码放在单独的一个页面里面测试是没问题的,这其中的原因令人费解。

虽然htmx在GitHub上的start已经有50多K,但网上的资料少之又少,特别是中文资料更是凤毛麟角。htmx官方上的示例也只是一笔带过,很多在实际应用当中是断层的,这让刚刚接触htmx这个库的朋友们甚是头大。

费了一番功夫,终于窥得天机,现在就一起揭开这一层纱。

先建一个演示的项目,使用Python + flask + htmx作为示例,项目总共两个子文件夹:templates、static;4个文件:main.py,templates/index.html,templates/login.html,static/htmx2.0.3.min.js,项目目录结构按flask的默认结构。

main.py代码:

from flask import Flask, render_template,request

app = Flask(__name__)

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


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return f"Hello {request.form['username']}"
    return render_template('login.html')


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

index.html代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="static/htmx2.0.3.min.js"></script>
    <title>Document</title>
</head>

<body>
    <div hx-get="login" hx-target="#login-model" hx-swap="innerHTML" hx-trigger="click">Login</div>
    <div><a href="#" onclick="getlogin()">Login By JavaScript</a></div>
    <div id="login-model"></div>
</body>
<script>
    function getlogin() {
        fetch('login').then(response => response.text()).then(data => {
            document.getElementById('login-model').innerHTML = data;
        });
    }
</script>

</html>

login.html代码:

<div style="margin: 20px;"><input type="text" id="username" name="username" placeholder="username" value="gethtmx.com"></div>
<div style="margin: 20px;" hx-post="login" hx-target="#login-model" hx-include="#username,#password">提交</div>

运行main.py,在浏览器上打开 http://localhost:5000,可以看到出现的 Login 和 Login By JavaScript两行文字。

Index

当点击“Login”时页面会加载一个表单,直接点击表单的“提交”按钮,页面会返回“Hello gethtmx.com”的文本。

表单

Hello gethtmx.com

但是,当我们点击“Login By JavaScript”时,虽然同样返回了相同的表单,但此时点表单的“提交”按钮却没反应。而且在开发者工具中没有看到有请求产生,后台也没有收到任何请求。但两次返回的表单代码都是相同的,只不过Login By JavaScript的表单是通过JavaScript原生代码fetch请求并插入的Dom。

被这个问题困扰了多日,后来终于在htmx官网上看到这样一段说明:

Method - htmx.process()

Processes new content, enabling htmx behavior. This can be useful if you have content that is added to the DOM outside of the normal htmx request cycle but still want htmx attributes to work.

这段话翻译过来的意思就是:

处理新内容,启用 htmx 行为。如果你在正常的 htmx 请求周期之外将内容添加到 DOM,但仍希望 htmx 属性正常工作,这将非常有用。

结合这段话下面的示例,正确的理解应该是:

使用原生JavaScript请求的html片段并生成的Dom,如果包含htmx行为,需要使用htmx.process()函数对 Dom 进行处理。

htmx.process(document.getElementById("dom-id"));

豁然开朗!

于是我们用这个方法对index.html中的getlogin()函数进行优化。

function getlogin() {
    fetch('login').then(response => response.text()).then(data => {
        document.getElementById('login-model').innerHTML = data;
        // 加入了以下这行代码
        htmx.process(document.getElementById('login-model'));
    });
}

重新运行项目,发现问题已经迎刃而解。

热门文章