ajax 重新加载 ul/li div 并保留事件侦听器(insertAdjacentHTML 除外)

ajax reload ul/li div and retain event listeners (other than insertAdjacentHTML)

目标:在 ajax 调用(添加或删除标签)后仅重新加载 div,而不会在重新加载的 div 上丢失事件侦听器.使用 insertAdjacentHTML 似乎不是最佳选择。

前端:我有一个带有 ul/li 标签的边栏。 add/create 标签有模态;每个标签旁边都有一个图标 "X",可在点击时将其删除。

<ul>
  <li><span>A</span><span class=li-untag>X</span> 
  <li><span>B</span><span class=li-untag>X</span> 
  <li><span>C</span><span class=li-untag>X</span> 
  <li><span>D</span><span class=li-untag>X</span>
</ul>

我失败的设置是:

  1. 向 php 后端发送 ajax(获取)调用以添加或删除标签

  2. 如果没有错误,后端会为文章发送更新后的 twig 格式的标签列表

  3. 将 div 更新为 innerHTML

innerHTML 更新将丢失列表的所有事件侦听器(例如单击 X 时删除标签),直到真正刷新页面。我在 ajax 调用后尝试 运行 addeventlisteners,但它没有用。

我当前的设置 基于之前的 SO 答案(见下文)使用 insertAdjacentHTML。那(有点)有效,但它很笨重。

我真的更希望 php 为 div 发送一个完整的更新和格式化的列表。

对于在 vanilla js 中执行此操作的更优雅的方法有何建议?

FYI: The main answers I have relied on:

Is it possible to append to innerHTML without destroying descendants' event listeners?
If a DOM Element is removed, are its listeners also removed from memory?

编辑:监听器附加到"li-untag" class

编辑 2:回答更多代码的评论请求。这是删除标签,使用 atomic ajax library:

for(let liUntag of document.querySelectorAll('.li-untag')){
liUntag.addEventListener("click", () => altUntag(liUntag))};

const altUntag = (el) => {
    atomic("/tags", {
        method: 'POST',
        data: {
            type: 'untag',
            slug: el.getAttribute('data-slug'),
            tag: el.getAttribute('data-untag')
        }
    }).then(function (response) {
        el.closest('li').style.display = 'none';
        console.log(response.data); // xhr.responseText
        console.log(response.xhr);  // full response
    })
    .catch(function (error) {
        console.log(error.status); // xhr.status
        console.log(error.statusText); // xhr.statusText
    });
};

最好的解决方案是使用事件委托将单个侦听器附加到 ul,而不是将多个侦听器附加到每个 li-untag:

document.querySelector('ul').addEventListener('click', ({ target }) => {
  if (target.matches('.li-untag')) {
    target.closest('li').remove();
  }
});

如果您不想这样做并希望在每个 li-untag 元素上保留一个侦听器,您可以插入新的 HTML,然后对 <li> 进行排序,并将监听器附加到每个新的 li-untag:

const addListeners = () => {
  document.querySelectorAll('.li-untag').forEach((span) => {
    span.addEventListener('click', ({ target }) => {
      target.closest('ul').remove();
    });
  });
};

// on document load:
addListeners();

// after inserting new HTML:
const lis = [...document.querySelectorAll('ul li')];
lis.sort((a, b) => a.children[0].textContent.localeCompare(b.children[0].textContent));
const ul = document.querySelector('ul');
lis.forEach((li) => {
  ul.appendChild(li);
});
addListeners();