在 DOM 中移动元素时,该元素是否保持状态

when moving element in DOM, does that element remain state?

本文关键字:元素 是否 状态 DOM 移动      更新时间:2023-09-26
<ul id="task1">
  <li><input type="checkbox" id="input1"><label>Item task1></label></li>
  ... some other <li> go here
</ul>
<ul id="task2">
  <li><input type="checkbox"><label>Item task2></label></li>  
   ...
</ul>
var task1 = document.getElementById("task1");
var task2 = document.getElementById("task2");
var moveFromTask1to2 = function() { task2.appendChild(this.parentNode); }
var moveFromTask2to1 = function() { task1.appendChild(this.parentNode); }
var bindElement(listItem, task) {
  var checkbox = listItem.querySelector("input[type=checkbox]");
  checkbox.onchange = task;
}
for (var i = 0; i < task1.children.length; i++) { bindElement(task1.children[i], moveFromTask1to2); //(1)
for (var i = 0; i < task2.children.length; i++) { bindElement(task2.children[i], moveFromTask2to1); //(2)

当我选中input1复选框时,它应该从task1移动到task2。它做到了。

但是当我再次选中input1复选框时,它不会从task2移回task1

谁能告诉我为什么?

问题是第一个列表#task1中的复选框#task2更改事件时移动到列表中。发生这种情况后,在后续更改事件中,复选框仍会移动到同一列表中,因为事件处理程序是相同的。这就是您无法将复选框移回初始列表的原因。

要修复它,您可以编写一个函数而不是两个moveFromTask1to2moveFromTask2to1。例如:

var moveFromTo = function() {
    var moveTo = this.parentNode.parentNode === task1 ? task2 : task1;
    moveTo.appendChild(this.parentNode);
};

查看下面的演示。

var task1 = document.getElementById("task1");
var task2 = document.getElementById("task2");
var moveFromTo = function() {
    var moveTo = this.parentNode.parentNode === task1 ? task2 : task1;
    moveTo.appendChild(this.parentNode);
}
var bindElement = function(listItem, task) {
    var checkbox = listItem.querySelector("input[type=checkbox]");
    checkbox.onchange = task;
}
for (var i = 0; i < task1.children.length; i++) { bindElement(task1.children[i], moveFromTo); } //(1)
for (var i = 0; i < task2.children.length; i++) { bindElement(task2.children[i], moveFromTo); } //(2)
<ul id="task1">
    <li>
        <input type="checkbox" id="input1" />
        <label>Item task11></label>
    </li>
    <li>
        <input type="checkbox" id="input1" />
        <label>Item task12></label>
    </li>
</ul>
<ul id="task2">
    <li>
        <input type="checkbox" />
        <label>Item task21></label>
    </li>
    <li>
        <input type="checkbox" />
        <label>Item task22></label>
    </li>
</ul>

您将事件处理程序绑定到名为 moveFromTask1to2 的对象。

您的任何代码都不会删除该事件处理程序并添加moveFromTask2to1

JavaScript不会进行时间旅行。它不会撤消您的for循环所执行的操作并重新运行它们,因为它们过去运行的数据从那时起发生了变化。


您可能应该通过使用单个事件处理程序函数来解决此问题,该函数不是硬编码 from 和 to 元素,而是根据 this.parentNode 的值计算它们。

问题是,当您单击复选框时,事件不会自动更改。它仍然具有相同的事件处理程序,这会将其从任务 1 移动到任务 2。

解决方案 1:在移动后分配新的事件处理程序:

var moveFromTask1to2 = function() { 
  task2.appendChild(this.parentNode); 
  this.onchange = moveFromTask2to1;
}
var moveFromTask2to1 = function() { 
  task1.appendChild(this.parentNode); 
  this.onchange = moveFromTask1to2;
}

解决方案 2:使事件处理程序更通用,使其成为单个"切换"函数:

var task1 = document.getElementById("task1");
var task2 = document.getElementById("task2");
var moveItem = function() { 
  var task = this.parentNode.parentNode;
  if (task == task1)
    task2.appendChild(this.parentNode);
  else
    task1.appendChild(this.parentNode);
}
var bindElement = function(listItem) {
  var checkbox = listItem.querySelector("input[type=checkbox]");
  checkbox.onchange = moveItem;
}
for (var i = 0; i < task1.children.length; i++) { bindElement(task1.children[i]);} //(1)
for (var i = 0; i < task2.children.length; i++) { bindElement(task2.children[i]);} //(2)
ul {
  border: 1px solid blue;
}
<ul id="task1">
  <li><input type="checkbox" id="input1"><label>Item task1></label></li>
 
</ul>
<ul id="task2">
  <li><input type="checkbox"><label>Item task2></label></li>  
   ...
</ul>

不过,您会注意到,移动有效,但复选框会记住状态(即"选中"(。您可能会在代码中重置它,但实际上我认为从 UX 的角度来看,这是一个非常糟糕的解决方案。复选框用于检查/选择元素。它不应该任何事情。如果希望能够将列表项移动到另一个列表,则应改用按钮。

不过,这并不能改变答案。如果将复选框更改为按钮并使用 onclick 事件而不是 onchange,则相同的答案适用。从技术上讲,它与上面的代码片段几乎相同,但从用户的角度来看,这更有意义:

var task1 = document.getElementById("task1");
var task2 = document.getElementById("task2");
var moveItem = function() { 
  var task = this.parentNode.parentNode;
  if (task == task1)
    task2.appendChild(this.parentNode);
  else
    task1.appendChild(this.parentNode);
}
var bindElement = function(listItem) {
  var button = listItem.querySelector("input[type=button]");
  button.onclick = moveItem;
}
for (var i = 0; i < task1.children.length; i++) { bindElement(task1.children[i]);} //(1)
for (var i = 0; i < task2.children.length; i++) { bindElement(task2.children[i]);} //(2)
ul {
  border: 1px solid blue;
}
<ul id="task1">
  <li><input type="button" value="Move"/><label>Item task1></label></li>
 
</ul>
<ul id="task2">
  <li><input type="button" value="Move"><label>Item task2></label></li>  
   ...
</ul>