Coding, Insights, and Digital Discoveries 👩🏻💻
From Stay-at-Home Mom to a Developer After 50
I am still working on improving my To-Do List app using vanilla JavaScript. So far, I have refreshed some core JavaScript concepts, integrated Tailwind CSS for styling, and made tasks editable. As I continue to enhance the project, I realized that adding a drag-and-drop feature would make the app more practical and user-friendly.
✅ Better User Experience – Users can reorder tasks easily.
✅ Persistent Order – The new arrangement is saved in localStorage
, so the order remains even after refreshing the page.
✅ Visual Feedback – Users see clear indicators showing where the task will be dropped.
To achieve this, I use the Drag and Drop API and attach event listeners to the <ul>
element rather than individual <li>
items. This ensures efficient event handling while keeping the code clean and scalable.
let draggedItem = null; // Stores the currently dragged item
This will temporarily store the <li>
element that is being dragged.
dragstart
(When Dragging Begins)function handleDragStart(e) {
draggedItem = e.target; // Store reference to dragged <li>
e.target.style.opacity = '0.5'; // Make it visually distinct
}
<li>
element in draggedItem.function getDropDetails(e) {
const targetItem = e.target.closest('li'); // Identify nearest <li> under cursor
if (!targetItem || targetItem === draggedItem) return null; // Ignore if no valid target
const targetRect = targetItem.getBoundingClientRect();
const midPoint = targetRect.top + targetRect.height / 2;
const insertAfter = e.clientY > midPoint; // Check if dragging below midpoint
return { targetItem, insertAfter };
}
<li>
under the cursor.getBoundingClientRect()
to get the midpoint of the target <li>
.function removeDropTargetStyles() {
document.querySelectorAll('li').forEach(item => {
item.style.borderTop = ''; // Reset borders
item.style.borderBottom = '';
});
}
dragover
(When Dragging Over Another Item)function handleDragOver(e) {
e.preventDefault(); // Allows dropping (default behavior blocks it)
removeDropTargetStyles(); // Clean up existing drop indicators
const details = getDropDetails(e); // Get target position
if (!details) return;
// Apply drop indicators based on position
details.targetItem.style.borderTop = details.insertAfter ? '' : '2px solid #666';
details.targetItem.style.borderBottom = details.insertAfter ? '2px solid #666' : '';
}
e.preventDefault()
allows dropping (by default, dropping is disabled).removeDropTargetStyles()
to avoid multiple borders appearing.getDropDetails(e)
to determine where to insert.drop
(When Dragging Stops and Item is Released)function handleDrop(e) {
e.preventDefault();
const details = getDropDetails(e);
if (!details) return;
// Move the dragged item before/after the target
if (details.insertAfter) {
details.targetItem.parentNode.insertBefore(draggedItem, details.targetItem.nextSibling);
} else {
details.targetItem.parentNode.insertBefore(draggedItem, details.targetItem);
}
updateTaskOrder(); // Save new order to localStorage
}
getDropDetails(e)
to find where to insert the dragged item.draggedItem
before or after targetItem
.updateTaskOrder()
to save the new order in localStorage
.dragend
(After Dragging is Finished)function handleDragEnd(e) {
e.target.style.opacity = ''; // Reset opacity
removeDropTargetStyles(); // Remove any remaining visual indicators
}
With this implementation, my To-Do List now supports smooth drag-and-drop reordering, and task order is maintained even after a page refresh.