原生JavaScript实现不同待办列表间列表项拖拽移动的技术问题
Hey there! Let's figure out why your current drag-and-drop only works partially and get you set up for free cross-list item movement with vanilla JavaScript. Here's what's going wrong and how to fix it:
Key Issues in Your Current Code
- You're only attaching drag events to one list (
let list = document.querySelector('.mylist')), so other lists won't handle drag/drop actions. - Your
isLiItemfunction restricts dragging to only checked items—remove that restriction if you want free movement. - The
onDragDropfunction hardcodes inserting into the first list (list.insertBefore(...)) instead of the list where the drop happens. - Your
onDragEndselector is broken ('.mylist .checked .unchecked'won't target any items correctly).
Step-by-Step Fixes
1. Update the Drag Event Logic
First, we'll refactor the code to handle all .mylist elements, not just the first one. We'll also remove the checked-item restriction (you can add it back later if needed by adjusting the isLiItem check).
2. Modify HTML to Include Multiple Lists
We'll add another todo list to demonstrate cross-list functionality.
3. Fix the addItem Function
Since you referenced addItem(this) in your form, we'll add a simple implementation for that too.
Complete Working Code
HTML
<div id="tasks"> <details> <summary>Tasks List 1</summary> <form class="additem" onsubmit="addItem(this, 'tasks_list'); return false;"> <input type="text" placeholder="Enter new task"> <input type="submit" value="Add New Task"> </form> <ul class="mylist" id="tasks_list" draggable="false"></ul> </details> <details> <summary>Tasks List 2</summary> <form class="additem" onsubmit="addItem(this, 'tasks_list_2'); return false;"> <input type="text" placeholder="Enter new task"> <input type="submit" value="Add New Task"> </form> <ul class="mylist" id="tasks_list_2" draggable="false"></ul> </details> </div> <style> /* Add some basic styling to see drag states */ .mylist { list-style: none; padding: 0; margin: 10px 0; } .mylist li { padding: 8px; border: 1px solid #eee; margin: 4px 0; cursor: grab; } .mylist li.checked { text-decoration: line-through; opacity: 0.7; } .on-drag-start { opacity: 0.5; cursor: grabbing; } .on-drag-enter { border-top: 2px solid #2196F3; } </style>
JavaScript
let draggedItem; // Updated: Allow any list item to be dragged (remove .checked check if you want free movement) function isLiItem(event) { return event.target && event.target.tagName === 'LI'; } function onDragStart(event) { if (isLiItem(event)) { event.target.classList.add('on-drag-start'); draggedItem = event.target; event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/html', event.target.innerHTML); } } function onDragEnter(event) { if (isLiItem(event)) { event.target.classList.add('on-drag-enter'); } } function onDragLeave(event) { if (isLiItem(event)) { event.stopPropagation(); event.target.classList.remove('on-drag-enter'); } } function onDragOver(event) { // Allow dropping on the list itself too, not just list items if (event.target.classList.contains('mylist') || isLiItem(event)) { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; } } function onDragDrop(event) { event.preventDefault(); // Get the target list (either the list itself or the parent of the list item) const targetList = event.target.classList.contains('mylist') ? event.target : event.target.closest('.mylist'); if (draggedItem && targetList) { // Insert before the target item if dropping on an item, else append to the list if (isLiItem(event)) { targetList.insertBefore(draggedItem, event.target); } else { targetList.appendChild(draggedItem); } draggedItem.classList.remove('on-drag-start'); // Clear all drag-enter classes document.querySelectorAll('.on-drag-enter').forEach(item => item.classList.remove('on-drag-enter')); } } function onDragEnd(event) { draggedItem?.classList.remove('on-drag-start'); document.querySelectorAll('.on-drag-enter').forEach(item => item.classList.remove('on-drag-enter')); } // Attach events to ALL .mylist elements document.querySelectorAll('.mylist').forEach(list => { list.addEventListener('dragstart', onDragStart); list.addEventListener('dragover', onDragOver); list.addEventListener('dragenter', onDragEnter); list.addEventListener('dragleave', onDragLeave); list.addEventListener('drop', onDragDrop); list.addEventListener('dragend', onDragEnd); }); // Implement the addItem function to add tasks to lists function addItem(form, listId) { const input = form.querySelector('input[type="text"]'); const taskText = input.value.trim(); if (taskText) { const li = document.createElement('li'); li.textContent = taskText; li.draggable = true; // Make sure new items are draggable // Add toggle for checked state (optional, if you want that functionality) li.addEventListener('click', () => li.classList.toggle('checked')); document.getElementById(listId).appendChild(li); input.value = ''; } }
What Changed?
- Multiple List Support: We use
document.querySelectorAll('.mylist')to attach events to every todo list. - Flexible Dropping: You can now drop items directly onto a list (not just between items) or between existing items.
- Free Drag: Removed the
.checkedrestriction inisLiItem—if you want to only allow checked items to be dragged, just change the function back toreturn event.target && event.target.classList.contains("checked");. - Fixed Drag Cleanup:
onDragEndnow properly clears all drag state classes from every item. - Working Add Item: The
addItemfunction creates draggable list items and adds them to the correct list.
Now you can drag any item between the two lists freely! Test it out by adding tasks to both lists and dragging items back and forth.
内容的提问来源于stack exchange,提问作者Исмет Брахимбашев

