javascript - 如何从列表中清除选定的元素

标签 javascript html css onclick addeventlistener

我无法使此功能正常工作。它不会删除我选择的 TODO 列表中的所有元素,而是删除其中的一半。如果我有一个包含 1 2 3 4 5 6 7 8 的列表,它将删除 1 3 5 7。

clearCompleted: function() {
       var clearCompleted = document.getElementById("clearCompleted");
       clearCompleted.addEventListener("click", function() {
         for (var i = 0; i < todoList.todos.length; i++) {
           todoList.todos[i]
           if (todoList.todos[i].completed === true) {
             todoList.deleteTodo(i);
            }
           }
              view.displayTodos()
         })
     }

   deleteTodo: function (position){
      this.todos.splice(position,1);
    },

最佳答案

let todos = [
  {"id": 0, "completed":  true, "name": "done :)"},
  {"id": 1, "completed":  true, "name": "done-skippedThisIndex :("},
  {"id": 2, "completed": false, "name": "working on it"}, 
  {"id": 3, "completed": false, "name": "incomplete"}, 
  {"id": 4, "completed":  true, "name": "finished"},  
  {"id": 5, "completed":  true, "name": "complete-skippedThisIndex :("},
  {"id": 6, "completed":  true, "name": "complete-neverSeesThisIndex :)"},  
  {"id": 7, "completed":  true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);

todos = todos.filter(function(todo, i){
  console.log(i, "of", todos.length, todo);
  return (todo.completed === false);
});

console.log("AFTER:(length:", todos.length, ")\n", todos);

注意:我只传入了 i,因此 console.log 可以显示所有数组项均已处理,以便与下一个示例中的代码(相当于您的代码)进行对比。
实际上,您只需要如下代码:

todos = todos.filter(function(todo){
  return (todo.completed === false);
});

使用 ES6 语法,这可以进一步缩短为:

todos = todos.filter(todo => todo.completed === false);
<小时/>

这是您的代码的等效项。如果检查 console.log 输出,您会发现当您从数组中删除元素时,i 不再与 id 数字匹配。这是因为随着数组中元素的前进,i 指向的值会发生变化。结果是,数组查看的下一个元素现在占据了当前i 的空间位置。现在,在循环的下一次传递中,i 递增,因此向前移动的元素永远不会被查看。这会导致某些元素被“跳过”。

let todos = [
  {"id": 0, "completed":  true, "name": "done :)"},
  {"id": 1, "completed":  true, "name": "done-skippedThisIndex :("},
  {"id": 2, "completed": false, "name": "working on it"}, 
  {"id": 3, "completed": false, "name": "incomplete"}, 
  {"id": 4, "completed":  true, "name": "finished"},  
  {"id": 5, "completed":  true, "name": "complete-skippedThisIndex :("},
  {"id": 6, "completed":  true, "name": "complete-neverSeesThisIndex :)"},  
  {"id": 7, "completed":  true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);

for (var i = 0; i < todos.length; i++) {
  if (todos[i].completed === true) {
    console.log(i,"of", todos.length, ": REMOVING", todos[i]);
    todos.splice(i, 1);
    console.log("    ", todos.length, "(new  length - next id will be skipped over)\n");
  } else {
    console.log(i,"of", todos.length, ": keeping", todos[i]);
    console.log("    ", todos.length, "(same length)\n");
  }
}

console.log("AFTER:(length:", todos.length, ")\n", todos);

<小时/>

要保留您编写的循环结构,只需更改您的代码,这样您就不会在循环遍历数组时更改数组。您可以创建一个新数组,并在其中填充要保留的元素。然后将新数组重新分配给旧变量:

let todos = [
  {"id": 0, "completed":  true, "name": "done :)"},
  {"id": 1, "completed":  true, "name": "done-skippedThisIndex :("},
  {"id": 2, "completed": false, "name": "working on it"}, 
  {"id": 3, "completed": false, "name": "incomplete"}, 
  {"id": 4, "completed":  true, "name": "finished"},  
  {"id": 5, "completed":  true, "name": "complete-skippedThisIndex :("},
  {"id": 6, "completed":  true, "name": "complete-neverSeesThisIndex :)"},  
  {"id": 7, "completed":  true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);

newTodos = [];
for (var i = 0; i < todos.length; i++) {
  if (todos[i].completed === false) {
    console.log(i,"of", todos.length, ": keeping", todos[i]);
    newTodos.push(todos[i]);
  }
  else {
    console.log(i,"of", todos.length, ": NOT keeping", todos[i]); 
  }
}
todos = newTodos;
console.log("AFTER:(length:", todos.length, ")\n", todos);

在这里您可以看到所有元素都被实际查看,没有跳过任何索引。
结果正是您想要的元素。

另一个“hacky”(不建议您使用)的方法是通过在拼接当前元素时递减当前索引来抵消更改下一个元素所在索引的影响。这样,在循环开始时,您将增加 i 以指向您想要查看的下一个元素:

let todos = [
  {"id": 0, "completed":  true, "name": "done :)"},
  {"id": 1, "completed":  true, "name": "done-skippedThisIndex :("},
  {"id": 2, "completed": false, "name": "working on it"}, 
  {"id": 3, "completed": false, "name": "incomplete"}, 
  {"id": 4, "completed":  true, "name": "finished"},  
  {"id": 5, "completed":  true, "name": "complete-skippedThisIndex :("},
  {"id": 6, "completed":  true, "name": "complete-neverSeesThisIndex :)"},  
  {"id": 7, "completed":  true, "name": "complete-lengthWasShortened :("}
];
//console.log("BEFORE:(length:", todos.length, ")\n", todos);

for (var i = 0; i < todos.length; i++) {
  if (todos[i].completed === true) {
    console.log(i,"of", todos.length, ": REMOVING", todos[i]);
    todos.splice(i, 1);
    i--;  // decrement i to reflect that the data moved backwards in the array
    console.log("    ", todos.length, "(new length)\n    ", i, "(new i)  decremented i to reflect that the data moved backwards/(to the left by 1) in the array. \n     Now, all items will still be looked at)\n");
  } else {
    console.log(i,"of", todos.length, ": keeping", todos[i]);
    console.log("    ", todos.length, "(same length)\n");
  }
}

console.log("AFTER:(length:", todos.length, ")\n", todos);

这是通过在执行 splice(i, 1) 后立即添加 i-- 来实现的,以反射(reflect)剩余数据现在恰好位于左侧 1 个位置以前的位置。这确保了所有数据都得到查看和评估。这不是处理这种情况的推荐方法,但对于其他棘手的情况可能会派上用场。

for (var i = 0; i < todos.length; i++) {
  if (todos[i].completed === true) {
    todos.splice(i, 1);
    i--;   // all the data moved left, update the index accordingly
  }
}

关于javascript - 如何从列表中清除选定的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59900664/

相关文章:

javascript - IE 11 无法提交 HTML 表单

javascript - 如何在 maquette js 中管理焦点和光标位置?

javascript - 页脚在中间加载然后粘在底部

css - ReactJS + Heroku : Why doesn't my website become mobile responsive?

html - 如何对齐不同字体大小的文本部分

php - Wordpress:用png图像更改图标[进入功能]

javascript - Angular 2 http Observable 请求不返回响应 header

javascript - 解析 JSON - eval() 或函数对象?

javascript - StopPropagation() 不工作

javascript - 隐藏div点击外面