如何对动态创建的 HTML 使用拖放? (可排序)

How do I use drag and drop with dynamically created HTML? (SortableJS)

我开始学习 javascript 并且我有一个简单的待办事项应用程序,我希望能够在其中拖放创建的不同待办事项。一种简单的方法是使用 SortableJS 库,但它无法按照我想要的方式工作。实现简单的 sortable 功能后,当拖动一个待办事项时,它会抓取整个待办事项列表而不是单个待办事项

我认为问题是因为我动态创建了 html,但我有点卡住了,非常感谢任何建议。

//Selectors
const todoInput = document.querySelector(".todos-input"); //input for adding a todo
const todoButton = document.querySelector(".todos-button"); //add todo-button
const todoList = document.querySelector(".todos-list"); //the todo-list

//Event listeners
todoButton.addEventListener("click", addTodo);
todoList.addEventListener("click", deleteTodo);
todoList.addEventListener("click", completeTodo);


//Functions
function addTodo(event) {
  //prevent form from submitting
  event.preventDefault();
  //create a div for the todos-list
  const todoDiv = document.createElement("div");
  //add classlist for styling
  todoDiv.classList.add("todo");
  //Create LI
  const newTodo = document.createElement("li");
  //output the value from the add-todo field
  if (todoInput.value != "") {
    newTodo.innerText = todoInput.value;
  } else {
    return false;
  }

  //classlist for styling
  newTodo.classList.add("todo-item");
  //append child to div

  todoDiv.appendChild(newTodo);
  //complete button
  const completedButton = document.createElement("button");
  completedButton.innerHTML = '<i class="fas fa-check"><i/>';
  completedButton.classList.add("completed-btn");
  todoDiv.appendChild(completedButton);
  //delete button
  const deletedButton = document.createElement("button");
  deletedButton.innerHTML = '<i class="fas fa-trash"><i/>';
  deletedButton.classList.add("deleted-btn");
  todoDiv.appendChild(deletedButton);

  //drag button
  const dragButton = document.createElement("button");
  dragButton.innerHTML = '<i class="icon fa fa-bars"><i/>';
  dragButton.classList.add("drag-btn");
  dragButton.classList.add("handle");
  todoDiv.appendChild(dragButton);

  //append div to list of todos
  todoList.appendChild(todoDiv);

  //clear input field after adding a new todo
  todoInput.value = "";

  //DRAG AND DROP
  const dragArea = document.querySelector('.todos-section');
  new Sortable(dragArea, {
    animation: 300
  });

}



//deleting todo
function deleteTodo(e) {
  //grab the item, whatever we are clicking on
  const item = e.target;
  //delete todo
  if (item.classList[0] === "deleted-btn") {
    //grab the parent element of the item, which is the todolist element in this case
    const todo = item.parentElement;
    //remove the todo
    todo.remove();
  }
}

//completing todo
function completeTodo(e) {
  //grab the item, whatever we are clicking on
  const item = e.target;
  //complete todo
  if (item.classList[0] === "completed-btn") {
    const todo = item.parentElement;
    //use the toggle because if the element has a class, then the classList.toggle method 
    //behaves like classList.remove and the class is removed from the element.
    //And if the element does not have the specified class
    //then classList.toggle, just like classList.add, adds this class to the element.
    //So it basically does the add/remove operation for us depending on the state.
    todo.classList.toggle("completed-todo");
  }
}
/*Apply to all elements*/

* {
  box-sizing: border-box;
  list-style-type: none;
  margin: 0;
  padding: 0;
}

body {
  font-family: "Merriweather Sans", sans-serif;
  background: rgba(216, 206, 206, 0.787);
}

.wrapper {
  display: flex;
  position: relative;
}


/* Add todos-section */

.todos-bar {
  position: fixed;
  top: 5%;
  left: 50%;
  font-size: 17px;
  border: 0;
  transform: translate(-50%, -50%);
  padding-left: 100px;
}

.todos-bar input {
  width: 600px;
  height: 50px;
  border: 0px;
  outline: none;
  font-size: 20px;
  padding-left: 20px;
  border-radius: 5px;
}

.todos-bar button {
  position: fixed;
  background: rgba(20, 33, 93, 0.952);
  color: white;
  font-size: 20px;
  border: 0;
  outline: none;
  height: 50px;
  padding: 10px 20px;
  right: 0px;
  border-radius: 0px 5px 5px 0px;
  cursor: pointer;
}

.todos-bar button:hover {
  background: rgb(43, 54, 73);
}


/* Todos section */

.todos-section {
  display: flex;
  position: fixed;
  top: 15%;
  left: 37%;
}

.todos-list {
  width: 600px;
}

.todo {
  margin: 1.5rem;
  background: white;
  color: black;
  font-size: 1.5rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-radius: 5px;
  padding-left: 0.5rem;
  margin: 15px;
  transition: all 0.5s ease;
}

.todo li {
  flex: 1;
}

.todo-item {
  padding: 0rem 0.5rem;
  padding-left: 2.5rem;
}

.deleted-btn,
.completed-btn {
  background: rgb(248, 56, 56);
  color: white;
  border: none;
  padding: 1rem;
  cursor: pointer;
  font-size: 1rem;
}

.completed-btn {
  background: green;
}

.deleted-btn {
  border-radius: 0px 5px 5px 0px;
}

.drag-btn {
  display: block;
  position: absolute;
  background: white;
  border: 2px solid white;
}

.fa-bars {
  padding: 5px;
  margin: 2px;
  cursor: pointer;
}

.fa-trash,
.fa-check {
  pointer-events: none;
}

.completed-todo {
  text-decoration: line-through;
  opacity: 0.5;
}
<head>
  <link rel="stylesheet" href="style.css">
  <script src="https://kit.fontawesome.com/47440aba67.js" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>

</head>

<body>
  <div class="wrapper">
    <!--ADD TODO-->
    <div class="todos-bar">
      <input type="text" class="todos-input" placeholder="Add to list...">
      <button class="todos-button" type="submit"><i class="fas fa-plus"></i></button>
    </div>
    <!--TODO LIST-->
    <div class="todos-section">
      <ul class="todos-list"></ul>
    </div>
  </div>
  <script src="script.js"></script>
</body>

</html>

根据documentation,

You can use any element for the list and its elements, not just ul/li

您实现的内容实际上符合此描述,因为里面有一个带有 div 标记的 ul。但是,您没有在 dragArea 中引用正确的元素,因为它应该是您想要的可拖动子项的直接父级 (.todos-list)。

因此,将其更改为 .todos-list 并另外将 handle 属性 传递给 Sortable 构造函数以引用要在其中拖动的 icon .

const dragArea = document.querySelector('.todos-list');
new Sortable(dragArea, {
    animation: 300,
    handle: '.fa-bars'
})

工作example