如何对动态创建的 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>
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
我开始学习 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>
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