如何删除包含动态生成的唯一ID的div?

发布于 2025-01-17 21:40:24 字数 2429 浏览 6 评论 0原文

我是JS的新手,对不起,如果我还不够清楚。

我正在创建一个待办事项列表。我已经为添加的每个新任务生成了一个DIV。每个Div都包含两个类。

  1. 一个
  2. 使用new Date()。getTime();

唯一的ID;现在,我需要允许删除特定的任务。但是似乎我无法使用唯一的ID删除DIV。也许是因为我没有收回数据?如何解决?

const myArray = [];

function myPushFunction(x){ // 3rd
    let itemToPush = myArray.push(x);
    return itemToPush;
}

function getMyInputValue(){
    let myInputValue = document.querySelector("#myInput").value;
    document.querySelector("#TasksList").innerHTML + myInputValue; 
    myPushFunction(myInputValue); 

    let taskScope = document.querySelector("#TasksList");

    let div1 = document.createElement("div");
    taskScope.appendChild(div1);


    let addItem = document.createTextNode(myInputValue);
    div1.appendChild(addItem);

    let idClass = "p" + new Date().getTime();
    div1.classList.add("taskdesign", idClass);

    let buttonDelete = document.createElement("BUTTON");
    div1.appendChild(buttonDelete);

    buttonDelete.classList.add("buttondeletedesign");

    let span = document.createElement("SPAN");
    span.innerHTML = "delete";
    buttonDelete.appendChild(span);

    span.classList.add("material-icons-outlined");

    buttonDelete.onclick = function(event) {
    const deleteElement = document.querySelector(`.${idClass}`);
        deleteElement.remove();
    }



}
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="todo-design.css">
    
    <link href="https://fonts.googleapis.com/css2?family=Jost:wght@500&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;400;700&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
    <title>Toudou</title>
</head>
<body>
<div id="myfullpage">
<h1>Toudou</h1>
    <div id="NewTask">
    <input type="text" id="myInput" placeholder="Task to add">
    <button id="button" onclick="getMyInputValue()">Add</button>
    </div>

    <div id="TasksList"></div>


    <script type="text/javascript" src="testjs-1.js"></script>
</div>
</body>
</html>

谢谢

I'm fairly new to JS, sorry if I'm not clear enough.

I'm creating a to do list. I've generated a div for every new task added. Each of those div contain two classes.

  1. a generic one
  2. a unique ID using new Date().getTime();

Now I need to allow to delete specific tasks. But it seems like I'm not able to delete a div using the unique ID. It's maybe because I'm not getting the data back? How to fix that?

const myArray = [];

function myPushFunction(x){ // 3rd
    let itemToPush = myArray.push(x);
    return itemToPush;
}

function getMyInputValue(){
    let myInputValue = document.querySelector("#myInput").value;
    document.querySelector("#TasksList").innerHTML + myInputValue; 
    myPushFunction(myInputValue); 

    let taskScope = document.querySelector("#TasksList");

    let div1 = document.createElement("div");
    taskScope.appendChild(div1);


    let addItem = document.createTextNode(myInputValue);
    div1.appendChild(addItem);

    let idClass = "p" + new Date().getTime();
    div1.classList.add("taskdesign", idClass);

    let buttonDelete = document.createElement("BUTTON");
    div1.appendChild(buttonDelete);

    buttonDelete.classList.add("buttondeletedesign");

    let span = document.createElement("SPAN");
    span.innerHTML = "delete";
    buttonDelete.appendChild(span);

    span.classList.add("material-icons-outlined");

    buttonDelete.onclick = function(event) {
    const deleteElement = document.querySelector(`.${idClass}`);
        deleteElement.remove();
    }



}
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="todo-design.css">
    
    <link href="https://fonts.googleapis.com/css2?family=Jost:wght@500&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;400;700&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
    <title>Toudou</title>
</head>
<body>
<div id="myfullpage">
<h1>Toudou</h1>
    <div id="NewTask">
    <input type="text" id="myInput" placeholder="Task to add">
    <button id="button" onclick="getMyInputValue()">Add</button>
    </div>

    <div id="TasksList"></div>


    <script type="text/javascript" src="testjs-1.js"></script>
</div>
</body>
</html>

Thanksss

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

水晶透心 2025-01-24 21:40:24

唯一 ID

您首先需要的是待办事项列表中任务的唯一 ID。时间并不是一个很好的选择,因为如果您以编程方式创建任务,则两个时间戳可能会获得相同的 ID。请参阅以下示例脚本,该脚本显示指令执行速度如此之快,以至于它们具有相同的时间戳。

const timestamp = new Date()
const timestamp2 = new Date()

console.log(timestamp.getTime())
console.log(timestamp2.getTime())

由于 JavaScript 是单线程,我们可以使用一个简单的计数器来代替。另一种可能性是UUID

在下面提供的实现中,我使用了一个简单的计数器。请参阅类 ToDoList 的私有类属性 #curId 来了解这一点。

任务

您应该将任务保存在一个数据结构中,例如一个简单的数组,以便以结构化形式准备好所有任务信息,供您的程序使用。这就是你的状态。使用它,管理您的应用程序比您看起来那样将这些信息放入 HTML 中的类、ID 等中要容易得多。

在下面提供的实现中,我使用了一个简单的数组来保存任务。请参阅 ToDoList 中的私有类属性 #tasks。我的实现中的任务只是我的 Task 类的一个实例,因此 #tasks 是一个由 Task 实例管理的数组ToDoList 类的实例。

怎么删除呢?

当您将删除按钮附加到每个任务并为该删除按钮附加一个事件处理程序时,就可以完成删除。每当单击该按钮时,都会触发事件处理程序并执行以下步骤:

  • 从任务数组(= 状态)中删除任务
  • 从视图(= HMTL 页面)中删除任务

为了识别任务,我们使用其唯一 ID我们将将该 ID 作为参数提供给删除按钮的 onclick 处理程序。我在创建删除按钮时执行此操作,因为此时我们知道该按钮将删除哪个任务。

请参阅下面提供的实现中 ToDoList 类的方法 #addTaskToView()#createDeleteButton() 按钮方法,了解我是如何完成此操作的。

实现:ToDo-List

我实现了一个非常简单的 ToDo-List,它只能从预定义的任务列表中删除任务(使用 UI),但可以轻松扩展它以能够添加任务以及更多任务。

我的实现是面向对象的,因为这对于给定的任务非常有效,甚至允许将待办事项列表视为应用程序中的一个独立组件,类似于 React 等前端框架的做法。我的代码片段展示了如何使用两个简单的命令 new ToDoList() 轻松创建两个 ToDoList。

您可能想阅读我在 MDN 文档中使用的一些功能,其中最重要的是:

/**
 * For demo purposes assume a user already had created these tasks
 */
const tasks = [
  {
    title: "My task 1",
    desc: "Desc for my task",
  },
  {
    title: "My task 2",
    desc: "Desc for my task 2",
  },
  {
    title: "My task dfssdf",
    desc: "Desc for my task fsddsfsd",
  },
  {
    title: "My task 1",
    desc: "Desc for my task",
  },
  {
    title: "My task 2",
    desc: "Desc for my task 2",
  },
  {
    title: "My task dfssdf",
    desc: "Desc for my task fsddsfsd",
  },
];

class Task {
  /**
   * @private
   * @type {number}
   */
  #id;
  /**
   * @private
   * @type {string}
   */
  #title;
  /**
   * @private
   * @type {string}
   */
  #description;

  constructor(id, title, description) {
    this.#id = id;
    this.#title = title;
    this.#description = description;
  }

  /**
   * Create a copy of an existing task.
   * @param {Task} task task to create a copy from
   * @returns deep copy of given task
   */
  static #from(task) {
    return new Task(task);
  }

  /**
   * Copy the task.
   * @returns 1:1 deep-copy of the task
   */
  clone() {
    return Task.#from(this);
  }

  get id() {
    return this.#id;
  }

  get title() {
    return this.#title;
  }

  get description() {
    return this.#description;
  }
}

class ToDoList {
  /**
   * List of tasks in this ToDo list.
   * @private
   * @type {Array<Task>}
   */
  #tasks;
  /**
   * Current ID. Counter used to assign a unique ID to a task within the list
   * @private
   * @type {number}
   */
  #curId;
  /**
   * <ul> tag that contains all list items
   * @private
   * @type {HTMLUListElement}
   */
  #list;
  /**
   * Root HTML element this list is attached to.
   * @private
   * @type {HTMLElement}
   */
  #root;

  /**
   * Create a new ToDo list.
   * @param {HTMLElement} attachTo root element this ToDo list is attached to
   * @param {string} title title for the ToDo list
   */
  constructor(attachTo, title) {
    this.#tasks = [];
    this.#curId = 1;
    if (!attachTo instanceof HTMLElement) throw "No valid wrapper element given!";
    this.#root = attachTo;
    this.#list = this.#createList()
    this.#addToDoListToView(title)
  }

  /**
   * Initialize the ToDo list 
   * @param {string} title title for todo list
   */
  #addToDoListToView(title){
    const header = this.#createToDoListHeader(title);
    this.#root.append(header, this.#list);
  }

  #createList(){
    return document.createElement("ul");
  }

  #createToDoListHeader(title){
    const heading = document.createElement("h1");
    heading.textContent = title;
    return heading;
  }

  get tasks() {
    // SoC: return copy; don't let callers manipulate the state of the actual task array
    return this.#tasks.slice();
  }

  get curId() {
    return this.#curId;
  }

  get wrapper() {
    return this.#list;
  }

  /**
   * Add a newly created task to the view.
   * @param {Task} newTask new task
   */
  #addTaskToView(newTask) {
    // new list item as a parent
    const listItem = document.createElement("li");
    listItem.classList.add("task");
    // a heading for the title
    const heading = document.createElement("h3");
    // create blockquote to show ID of a task
    const taskId = document.createElement("span");
    taskId.classList.add("id");
    // a paragraph for the description
    const desc = document.createElement("p");
    // create the delete button
    const deleteBtn = this.#createDeleteButton(newTask, listItem);

    // set text values for created elements
    heading.textContent = newTask.title;
    desc.textContent = newTask.description;
    taskId.textContent = `Task-ID: ${newTask.id}`;

    // build structure
    listItem.append(taskId, heading, desc, deleteBtn);
    // append to actual TODO list
    this.#list.appendChild(listItem);
  }

  /**
   * Create the delete button for a task.
   * @param {Task} newTask new task
   */
  #createDeleteButton(newTask, listItem) {
    // create a Button to delete the task
    const deleteBtn = document.createElement("button");
    deleteBtn.textContent = "Delete";
    // here is the important part! Pass the task ID to the delete function that will be called whenever the button is clicked
    deleteBtn.onclick = () => {
      const success = this.deleteTaskById(newTask.id);
      if (!success) {
        alert(
          `Task "ID: ${newTask.id} | ${newTask.title} does not exits and therefore could not be deleted!`
        );
        return;
      }
      this.#deleteTaskFromView(listItem);
    };
    return deleteBtn;
  }

  /**
   *
   * @param {string} title title for the task
   * @param {string} description description for the task
   * @returns newly created task
   */
  addTask(title, description) {
    // better to use an ID that is unique (one could also use UUIDs but for simplicity just count the IDs up)
    const newTask = new Task(this.#curId++, title, description);
    this.#tasks.push(newTask);
    // now add the task to the HTML document
    this.#addTaskToView(newTask);
    // return a copy of the newly created task, SoC: don't allow callers to manipulate internal objects
    return newTask.clone();
  }

  /**
   * Delete a task given its ID.
   * @param {number} id unique ID for a task
   * @returns true if task with given ID was found and deleted, false otherwise
   */
  deleteTaskById(id) {
    const foundIdx = this.#tasks.findIndex((task) => task.id === id);
    if (foundIdx !== -1) {
      this.#tasks.splice(foundIdx, 1)[0];
      return true;
    }
    return false;
  }

  /**
   * Delete a task from the HTML document using it's ID
   * @param {HTMLUListElement} listItem list element containing this task
   */
  #deleteTaskFromView(listItem) {
    listItem.parentNode.removeChild(listItem);
  }
}

window.addEventListener("DOMContentLoaded", (event) => {
  // container which holds the TODO list
  const container = document.getElementById("container1");
  const container2 = document.getElementById("container2");

  // create the todo list and specify the HTML element it should be attached to
  const todoList = new ToDoList(container, "My ToDo List");
  const todoList2 = new ToDoList(container, "2nd ToDo List");

  // add task list with some predefined tasks
  tasks.forEach((task) => {
    todoList.addTask(task.title, task.desc)
    todoList2.addTask(task.title, task.desc)
  });
});
.task{
  border-radius: 0.5rem;
  border: 1px solid black;
  list-style: none;
  padding: 1rem;
  margin: 0.5rem;
}

.id {
  font-size: small;
  color: gray;
}
<div id="container1"></div>
<div id="container2"></div>

Unique IDs

The first thing you need are unique IDs for a task in the ToDo list. Time is not a very good candidate as if you create tasks programmatically it's possible two timestamps get the same ID. See the following sample script that shows that the instructions are executed so fast that they have the same timestamp.

const timestamp = new Date()
const timestamp2 = new Date()

console.log(timestamp.getTime())
console.log(timestamp2.getTime())

As JavaScript is single threaded we could use a simple counter instead. Another possibility would be UUIDs.

In my implementation provided below I have used a simple counter. See private class attribute #curId of class ToDoList for this.

Tasks

You should keep tasks in a data structure e.g. a simple array to have all the task information ready in a structured form for your program to work with. This is your state. Using that, it is way easier to manage your application than just putting this information in classes, IDs and so on within the HTML as you seem to do it.

In my implementation provided below I have used a simple array to hold the tasks. See private class attribute #tasks in ToDoList. A task in my implementation is just an instance of the my Task class, consequently #tasks is an array of Task instances that are manages by an instance of the ToDoList class.

How to delete?

Deletion can be done when you attach a delete button to every task and to that delete button you attach an event handler. Whenever that button is clicked, the event handler will be triggered and should execute the following steps:

  • delete the task from your task array (= state)
  • delete the task from the view (= HMTL page)

To identify the task we use its unique ID and we will provide that ID as a parameter to the onclick handler of the delete button. I do this when we create the delete button cause at this time we know which task this button is going to delete.

Please see methods #addTaskToView() and #createDeleteButton() button methods of class ToDoList in my implementation provided below to see how I have done this.

Implementation: ToDo-List

My implementation of a very simple ToDo-List which can only delete tasks (using the UI) from a predefined list of task, but can easily be extended it to be able to add task and much more than that.

My implementation is object-oriented as this is works quite well for the given task and even allows to treat a ToDo-list as one independent component in your application similar to how frontend frameworks like React would do it. My snippet shows how this can be useful to effortless create two ToDoList with two simple commands new ToDoList().

You may want to read on some of the feature I have used in the MDN docs with the most important being:

/**
 * For demo purposes assume a user already had created these tasks
 */
const tasks = [
  {
    title: "My task 1",
    desc: "Desc for my task",
  },
  {
    title: "My task 2",
    desc: "Desc for my task 2",
  },
  {
    title: "My task dfssdf",
    desc: "Desc for my task fsddsfsd",
  },
  {
    title: "My task 1",
    desc: "Desc for my task",
  },
  {
    title: "My task 2",
    desc: "Desc for my task 2",
  },
  {
    title: "My task dfssdf",
    desc: "Desc for my task fsddsfsd",
  },
];

class Task {
  /**
   * @private
   * @type {number}
   */
  #id;
  /**
   * @private
   * @type {string}
   */
  #title;
  /**
   * @private
   * @type {string}
   */
  #description;

  constructor(id, title, description) {
    this.#id = id;
    this.#title = title;
    this.#description = description;
  }

  /**
   * Create a copy of an existing task.
   * @param {Task} task task to create a copy from
   * @returns deep copy of given task
   */
  static #from(task) {
    return new Task(task);
  }

  /**
   * Copy the task.
   * @returns 1:1 deep-copy of the task
   */
  clone() {
    return Task.#from(this);
  }

  get id() {
    return this.#id;
  }

  get title() {
    return this.#title;
  }

  get description() {
    return this.#description;
  }
}

class ToDoList {
  /**
   * List of tasks in this ToDo list.
   * @private
   * @type {Array<Task>}
   */
  #tasks;
  /**
   * Current ID. Counter used to assign a unique ID to a task within the list
   * @private
   * @type {number}
   */
  #curId;
  /**
   * <ul> tag that contains all list items
   * @private
   * @type {HTMLUListElement}
   */
  #list;
  /**
   * Root HTML element this list is attached to.
   * @private
   * @type {HTMLElement}
   */
  #root;

  /**
   * Create a new ToDo list.
   * @param {HTMLElement} attachTo root element this ToDo list is attached to
   * @param {string} title title for the ToDo list
   */
  constructor(attachTo, title) {
    this.#tasks = [];
    this.#curId = 1;
    if (!attachTo instanceof HTMLElement) throw "No valid wrapper element given!";
    this.#root = attachTo;
    this.#list = this.#createList()
    this.#addToDoListToView(title)
  }

  /**
   * Initialize the ToDo list 
   * @param {string} title title for todo list
   */
  #addToDoListToView(title){
    const header = this.#createToDoListHeader(title);
    this.#root.append(header, this.#list);
  }

  #createList(){
    return document.createElement("ul");
  }

  #createToDoListHeader(title){
    const heading = document.createElement("h1");
    heading.textContent = title;
    return heading;
  }

  get tasks() {
    // SoC: return copy; don't let callers manipulate the state of the actual task array
    return this.#tasks.slice();
  }

  get curId() {
    return this.#curId;
  }

  get wrapper() {
    return this.#list;
  }

  /**
   * Add a newly created task to the view.
   * @param {Task} newTask new task
   */
  #addTaskToView(newTask) {
    // new list item as a parent
    const listItem = document.createElement("li");
    listItem.classList.add("task");
    // a heading for the title
    const heading = document.createElement("h3");
    // create blockquote to show ID of a task
    const taskId = document.createElement("span");
    taskId.classList.add("id");
    // a paragraph for the description
    const desc = document.createElement("p");
    // create the delete button
    const deleteBtn = this.#createDeleteButton(newTask, listItem);

    // set text values for created elements
    heading.textContent = newTask.title;
    desc.textContent = newTask.description;
    taskId.textContent = `Task-ID: ${newTask.id}`;

    // build structure
    listItem.append(taskId, heading, desc, deleteBtn);
    // append to actual TODO list
    this.#list.appendChild(listItem);
  }

  /**
   * Create the delete button for a task.
   * @param {Task} newTask new task
   */
  #createDeleteButton(newTask, listItem) {
    // create a Button to delete the task
    const deleteBtn = document.createElement("button");
    deleteBtn.textContent = "Delete";
    // here is the important part! Pass the task ID to the delete function that will be called whenever the button is clicked
    deleteBtn.onclick = () => {
      const success = this.deleteTaskById(newTask.id);
      if (!success) {
        alert(
          `Task "ID: ${newTask.id} | ${newTask.title} does not exits and therefore could not be deleted!`
        );
        return;
      }
      this.#deleteTaskFromView(listItem);
    };
    return deleteBtn;
  }

  /**
   *
   * @param {string} title title for the task
   * @param {string} description description for the task
   * @returns newly created task
   */
  addTask(title, description) {
    // better to use an ID that is unique (one could also use UUIDs but for simplicity just count the IDs up)
    const newTask = new Task(this.#curId++, title, description);
    this.#tasks.push(newTask);
    // now add the task to the HTML document
    this.#addTaskToView(newTask);
    // return a copy of the newly created task, SoC: don't allow callers to manipulate internal objects
    return newTask.clone();
  }

  /**
   * Delete a task given its ID.
   * @param {number} id unique ID for a task
   * @returns true if task with given ID was found and deleted, false otherwise
   */
  deleteTaskById(id) {
    const foundIdx = this.#tasks.findIndex((task) => task.id === id);
    if (foundIdx !== -1) {
      this.#tasks.splice(foundIdx, 1)[0];
      return true;
    }
    return false;
  }

  /**
   * Delete a task from the HTML document using it's ID
   * @param {HTMLUListElement} listItem list element containing this task
   */
  #deleteTaskFromView(listItem) {
    listItem.parentNode.removeChild(listItem);
  }
}

window.addEventListener("DOMContentLoaded", (event) => {
  // container which holds the TODO list
  const container = document.getElementById("container1");
  const container2 = document.getElementById("container2");

  // create the todo list and specify the HTML element it should be attached to
  const todoList = new ToDoList(container, "My ToDo List");
  const todoList2 = new ToDoList(container, "2nd ToDo List");

  // add task list with some predefined tasks
  tasks.forEach((task) => {
    todoList.addTask(task.title, task.desc)
    todoList2.addTask(task.title, task.desc)
  });
});
.task{
  border-radius: 0.5rem;
  border: 1px solid black;
  list-style: none;
  padding: 1rem;
  margin: 0.5rem;
}

.id {
  font-size: small;
  color: gray;
}
<div id="container1"></div>
<div id="container2"></div>

画骨成沙 2025-01-24 21:40:24

不使用ID以及如何在点击处理程序中找到正确的元素的示例。

const deleteButtons = document.querySelectorAll("button.deleteBtn");

for (let d of deleteButtons) {
  d.addEventListener("click", (e) => {
    // e.target is the button that was clicked!
    let theButton = e.target;
    // theButton.parentNode is the div that the button is in!
    let theDiv = theButton.parentNode;
    theDiv.remove();
  });
}
<div id="myfullpage">
  <h1>Toudou</h1>

  <div id="TasksList">
    <div class="task">
      <span>task1</span>
      <button class='deleteBtn'>Delete</button>
    </div>
    <div class="task">
      <span>task2</span>
      <button class='deleteBtn'>Delete</button>
    </div>
    <div class="task">
      <span>task3</span>
      <button class='deleteBtn'>Delete</button>
    </div>
  </div>
</div>

Example of not using IDs and how to locate the right elements within the click handler.

const deleteButtons = document.querySelectorAll("button.deleteBtn");

for (let d of deleteButtons) {
  d.addEventListener("click", (e) => {
    // e.target is the button that was clicked!
    let theButton = e.target;
    // theButton.parentNode is the div that the button is in!
    let theDiv = theButton.parentNode;
    theDiv.remove();
  });
}
<div id="myfullpage">
  <h1>Toudou</h1>

  <div id="TasksList">
    <div class="task">
      <span>task1</span>
      <button class='deleteBtn'>Delete</button>
    </div>
    <div class="task">
      <span>task2</span>
      <button class='deleteBtn'>Delete</button>
    </div>
    <div class="task">
      <span>task3</span>
      <button class='deleteBtn'>Delete</button>
    </div>
  </div>
</div>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文