如何在动态创建的 HTML 中使用拖放功能? (SortableJS)

Posted

技术标签:

【中文标题】如何在动态创建的 HTML 中使用拖放功能? (SortableJS)【英文标题】:How do I use drag and drop with dynamically created HTML? (SortableJS) 【发布时间】:2021-11-05 16:23:42 【问题描述】:

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

我认为问题在于我动态创建了 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>

【问题讨论】:

【参考方案1】:

根据documentation,

您可以为列表及其元素使用任何元素,而不仅仅是 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

【讨论】:

以上是关于如何在动态创建的 HTML 中使用拖放功能? (SortableJS)的主要内容,如果未能解决你的问题,请参考以下文章

拖放引导上的动态网格

如何在角度7中将动态值设置为formControl

跨页面拖放div?

在 dom 树中的特定位置推送动态创建的删除元素

如何使用LightningChart拖放功能进行数据转移 ?

Angular 7 拖放 - 动态创建拖放区