带有复选框的删除功能和 JS 无法正常工作

Delete function with checkbox and JS doesn't work properly

目前我正在制作自己的项目,但遇到了问题。我为 Select/Unselect/Delete 复选框(包括其祖父元素(.layout))编写了如下代码。

然而,不知何故它只工作了 50%。当我创建很多复选框并尝试 select 或取消 select 它们时,它工作得很好。但是,如果我在 select 将它们全部删除后尝试删除它们,它只会删除其中的一半,然后我再次单击它,它会删除剩下的一半,然后一遍又一遍地执行此操作,直到只剩下一个复选框。这不是我遇到的唯一问题。当只有一个复选框时,我无法 select、unselect 或使用我制作的按钮将其删除。你能告诉我我的代码有什么问题以及如何解决吗?

//P.S 下面的代码只是我总结代码的一部分,所以我可以向您展示我的问题的确切部分。如果您需要完整的代码,请给我发消息,我会告诉您:)

谢谢。

const selectAll = document.querySelector(".select");
const unselectAll = document.querySelector(".unselect");
const deleteIndex = document.querySelector(".delete");

selectAll.addEventListener("click", selectAllindex);
unselectAll.addEventListener("click", unselectAllindex);
deleteIndex.addEventListener("click", deleteDiaryIndex);

function selectAllindex(i) {
  i.preventDefault();
  for (i = 0; i < testform.checkboxchild.length; i++) {
    console.log(testform.checkboxchild.length);
    testform.checkboxchild[i].checked = true;
  }
}
function unselectAllindex(i) {
  i.preventDefault();
  for (i = 0; i < testform.checkboxchild.length; i++) {
    testform.checkboxchild[i].checked = false;
  }
}

function deleteDiaryIndex(i) {
  i.preventDefault();
  for (i = 0; i < testform.checkboxchild.length; i++) {
    if (testform.checkboxchild[i].checked) {
      removeDiv(testform.checkboxchild[i]);
    }
  }
}

function removeDiv(d) {
  console.log(d.parentElement.parentElement);
  d.parentElement.remove();
}
.trash{
  display: none;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./test.css" />
  </head>
  <body>
    <form action="" name="testform" class="test">
      <div class="divs">
        <div class="layout">
          <div class="checkboxs">
            <input type="checkbox" name="checkboxchild" id="" />
          </div>
        </div>
        <div class="layout">
          <div class="checkboxs">
            <input type="checkbox" name="checkboxchild" id="" />
          </div>
        </div>
        <div class="layout">
          <div class="checkboxs">
            <input type="checkbox" name="checkboxchild" id="" />
          </div>
        </div>
        <div class="layout">
          <div class="checkboxs">
            <input type="checkbox" name="checkboxchild" id="" />
          </div>
        </div>
      </div>
      <div class="buttons">
        <button class="selector select" value="selectall">Select All</button>
        <button class="selector unselect" value="unselectall">
          Unselect All
        </button>
        <button class="selector delete" value="delete">Delete</button>
      </div>
    </form>

    <script src="./test.js"></script>
  </body>
</html>

if I try to delete them after selecting them all, it only deletes half of them, and I click it again, and it deletes half of them remain and do this over and over until only one checkbox is left.

发生这种情况是因为您在遍历它时改变 DOM 个节点 testform.checkboxchild

function deleteDiaryIndex(i) {
  i.preventDefault();

  console.log(testform.checkboxchild instanceof HTMLCollection);  // false
  console.log(testform.checkboxchild instanceof NodeList);        // true
  console.log(Object.prototype.toString.call(testform.checkboxchild));
  // '[object RadioNodeList]' (in both Chrome and Firefox, strangely)

  for (i = 0; i < testform.checkboxchild.length; i++) {
    if (testform.checkboxchild[i].checked) {
      removeDiv(testform.checkboxchild[i]);
    }
  }
}

这个 table 试图展示当所有复选框都被选中时,在上述函数中迭代时会发生什么。

i = 0 i = 1 i = 2
1st checkBox is checkboxchild[0] removed
2nd checkBox is checkboxchild[1] checkboxchild[0] checkboxchild[0]
3rd checkBox is checkboxchild[2] checkboxchild[1] removed
4th checkBox is checkboxchild[3] checkboxchild[2] checkboxchild[1]
checkboxchild.length 4 3 2

只有两个复选框被删除,因为 lengthtestform.checkboxchild 的索引在从 DOM 中删除每个复选框后会自动更新,所以什么是索引删除的复选框成为集合中下一个复选框的索引。

例如,第二个复选框没有被删除,因为在循环的第二次迭代中,当 i = 1checkboxchild[1] 指向第三个复选框,而不是第二个!

另请注意,当 i 变为 2 时循环结束,因为它不小于长度,即删除两个复选框后的 2

上述问题的一个解决方案是在迭代之前将活动集合转换为普通数组,例如

const checkboxes = Array.from(testform.checkboxchild);

另一种解决方案是根本不使用 testform.checkboxchild,而是使用 querySelectorAll,其中 returns 是节点的静态列表而不是实时节点列表。

const checkboxes = document.querySelectorAll('input[type=checkbox]');
for (let i = 0; i < checkboxes.length; i++) {
    if (checkboxes[i].checked) {
      removeDiv(checkboxes[i]);
    }
  }
}

const checkedboxes = document.querySelectorAll('input[type=checkbox]:checked');
for (const cb of checkedboxes) {
  removeDiv(cb);
}

您可能还想考虑使用 display: nonevisibility: hidden 样式作为从文档中完全删除复选框的一种易于逆转的替代方法。

When there is only a single checkbox, I can not select, unselect, or delete it with buttons I made

这是使用 testform.checkboxchild 引用复选框的另一个问题。

如果只剩下一个复选框,那么 testform.checkboxchild 将指向单个复选框而不是一个复选框列表,因此它不会有 length 属性 并且是可迭代的.

一个简单的解决方案是先检查 length 属性 是否存在。例如

function selectAllindex(event) {
  event.preventDefault();
  if (testform.checkboxchild.length !== undefined) {
     for (let i = 0; i < testform.checkboxchild.length; i++) {
       testform.checkboxchild[i].checked = true;
     }  
  } else {
    testform.checkboxchild.checked = true;
  }
}

function selectAllindex(event) {
  event.preventDefault();
  const checkboxes = document.querySelectorAll('input[type=checkbox]');
  for (const cb of checkboxes) {
    cb.checked = true;
  }
}

我的建议是:不要使用 testform.checkboxchild 来引用复选框。