I’m trying to create a todo list then use drag and drop to arrange it as I want. I have a dummy data (list items) which I can arrange as I want. the problem I am having is any new todo I add to the list item can not be dragged or rearrange.
here is my code below
var form = document.getElementById('addForm');
var itemList = document.getElementById('items');
form.addEventListener('submit', addTodo);
itemList.addEventListener('click', removeItem)
function addTodo(e) {
e.preventDefault()
// get input value
var newTodo = document.getElementById('todo');
// create new li element
var li = document.createElement('li')
//add class draggable property
li.className = 'draggable';
li.draggable = true
// add textnode with input value
li.appendChild(document.createTextNode(newTodo.value))
// delete button
var delBtn = document.createElement('button')
delBtn.className = 'btn btn-danger btn-sm float-right del';
delBtn.appendChild(document.createTextNode('X'))
li.appendChild(delBtn)
itemList.appendChild(li)
newTodo.value = ""
}
function removeItem(e) {
if (e.target.classList.contains('del')) {
if (confirm('Are you sure?')) {
var li = e.target.parentElement
itemList.removeChild(li)
}
}
}
const draggables = document.querySelectorAll('.draggable')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
itemList.addEventListener('dragover', (e) => {
e.preventDefault()
const draggable = document.querySelector('.dragging')
const afterElement = getDragAfterElement(draggable, e.clientY)
console.log(afterElement);
if (afterElement == null) {
itemList.appendChild(draggable)
} else {
itemList.insertBefore(draggable, afterElement)
}
})
function getDragAfterElement(draggables, y) {
const draggableElements = [...document.querySelectorAll('.draggable:not(.dragging)')]
console.log('Dragable', draggableElements);
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return {
offset: offset,
element: child
}
} else {
return closest
}
}, {
offset: Number.NEGATIVE_INFINITY
}).element
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo task</title>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css">
<!-- Google Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap">
<!-- Bootstrap core CSS -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet">
<!-- Material Design Bootstrap -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.19.1/css/mdb.min.css" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container mt-2">
<div class="row justify-content-center">
<div class="col-md-8">
<h1>Todo Task</h1>
<div class="card">
<div class="card-header bg-grey">
<h4>What Todo</h4>
</div>
<div class="card-body">
<form action="" id="addForm">
<div class="form-group mt-2 pl-5">
<label for="">Title of Task</label>
<input type="text" id="todo" placeholder="Enter todo..." class="form-control">
</div>
<button type="submit" id="submit" class="btn btn-primary btn-sm">Submit</button>
</form>
</div>
</div>
<div class="jumb mt-4">
<h2>Todo Lists</h2>
<ul class="list-group" id="items">
<li class="draggable" draggable="true">Item 1 <button class="btn btn-danger btn-sm float-right del">X</button></li>
<li class="draggable" draggable="true">Item 2 <button class="btn btn-danger btn-sm float-right del">X</button></li>
<li class="draggable" draggable="true">Item 3 <button class="btn btn-danger btn-sm float-right del">X</button></li>
</ul>
</div>
</div>
</div>
</div>
<!-- JQuery -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- Bootstrap tooltips -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.4/umd/popper.min.js"></script>
<!-- Bootstrap core JavaScript -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"></script>
<!-- MDB core JavaScript -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mdbootstrap/4.19.1/js/mdb.min.js"></script>
<script src="app.js"></script>
</body>
</html>
2
Answers
If I understand it correctly you are attaching some events to every item on the list to allow the drag&drop functionality.
The new elements don’t have those events attached to them, though. You need to watch for new elements and initialize the drag&drop functionality for them too.
NOTICE: Heretic Monkey has linked to a more comprehensive answer.
You need to warp this piece of into function
To
and call the function in
addTodo(e)
that is called event bindingalso, call the function on page load show it should bind the drag events by default
Complete JS.