首页 > 解决方案 > 使用 Handlebar.js 模板为多个按钮添加 onclick 事件监听器,只有最后一个有效

问题描述

我正在编写一个 Web 应用程序作为家庭作业项目,它使用 websocket 从服务器接收消息并使用 handlebars.js 在 html 上显示它们。

我正在尝试使用以下代码实现删除消息的功能:

document.addEventListener('DOMContentLoaded', () => {
    ...
    let message_container = document.querySelector("#message_container");
    const message_template = Handlebars.compile(document.querySelector("#message_template").innerHTML);
    const delete_button_template = Handlebars.compile(document.querySelector("#delete_button_template").innerHTML);

    function addDeleteButton(id) {
        let con = delete_button_template({"message_id": id}); //line #1
        document.querySelector("#content_" + id).innerHTML += con;
        document.querySelector("#delete_button_" + id).onclick = function () {
            const msg_id = event.target.dataset.message_id;
            const msg = document.querySelector("#message_" + msg_id);
            const blank = document.querySelector("#message_" + msg_id + "_newline");
            msg.parentElement.removeChild(msg);
            blank.parentElement.removeChild(blank);
        }
    }

将消息添加到 DOM 的函数很简单:

function newMessage(i) {
            let msg = message_template({
                "message_id": i.message_id,
                "username": ...,
                "timestamp": ...,
                "content": ...
            });
            message_container.innerHTML += msg;
            addDeleteButton(i.message_id);
    }

删除按钮的 handlebars.js 模板插入到 HTML 中,如下所示:

<button id="delete_button_{{ message_id }}" data-message_id="{{ message_id }}" class="btn btn-primary">
                Delete
</button>

但是,每次我向 DOM 添加新消息(包括删除按钮)时,所有以前的按钮都会失去其 onclick() 事件侦听器。chrome 检查器清楚地显示了这一点,它发生在第 1 行:
第 4 个按钮注册了一个事件侦听器。

添加第 5 个按钮

第4个按钮的onclick事件消失了。

一个明确的解决方法是将 onclick 调用嵌入到 html 属性中。但是为什么我不能像给定的代码一样注册事件监听器呢?

标签: javascripthtmlhandlebars.js

解决方案


问题出在以下行:

message_container.innerHTML += msg;

我们正在使用 Handlebars 生成一个新的 HTML 字符串,并将其存储到msg变量中。然后,我们希望将新的 HTML附加#message_containerDOM 元素。

但是,message_container.innerHTML += msg不会简单地将我们的新 HTML 附加到#message_container,而是替换 ! 的所有后代 DOM 节点 #message_container

发生的事情的细分是:

  • 我们得到#message_containerDOM 中的后代节点,将其字符串化为 HTML 字符串。
  • msg被连接到这个新的 HTML 字符串的末尾。
  • 新的 HTML 字符串替换innerHTMLof #message_container

这里的重要部分是替换。当innerHTMLof#message_container被替换时,它的所有后代节点都被替换。因此<button>,您之前附加的onclick侦听器的所有 s 都已从 DOM 中删除。

防止#message_container每次我们想要添加 a 时替换所有子项的一种<button>方法是使用Node.appendChild()API。结果代码如下所示:

// message_container.innerHTML += msg; // TODO: Replace this line with the below.

const node = document.createElement('div');
node.innerHTML = msg;
message_container.appendChild(node);

我创建了一个小提琴供参考。


推荐阅读