移动端Drag and Drop功能失效求助:已加Touch事件仍无法运行
移动端拖拽功能修复方案
核心问题分析
你的代码在移动端失效的核心原因是Touch事件体系没有dataTransfer对象,原代码中依赖event.dataTransfer传递拖拽元素ID的逻辑在触摸场景下完全无法工作。另外,Touch事件的触发逻辑和桌面Drag事件不匹配,直接复用事件回调会导致目标识别、状态跟踪混乱。
具体修复步骤
1. 替换dataTransfer,用全局变量存储拖拽元素
Touch事件没有dataTransfer,改用全局变量记录当前被拖拽的元素:
// 新增全局变量存储当前拖拽元素 let currentDraggedElement = null; function dragStart(event) { // 区分Drag和Touch事件逻辑 if (event.type === 'dragstart') { event.dataTransfer.setData('text/plain', event.target.id); } else { // Touch事件下直接存储元素引用 currentDraggedElement = event.target; // 阻止浏览器默认触摸行为(如滚动、缩放) event.preventDefault(); } setTimeout(() => { event.target.classList.add('hide'); }, 0); }
2. 修正Drop事件的元素获取逻辑
在Touch触发的drop事件中,从全局变量获取拖拽元素,替代dataTransfer:
function drop(event) { event.preventDefault(); // 区分Drag和Touch事件,获取拖拽元素 let draggedElement; if (event.type === 'drop') { const id = event.dataTransfer.getData("text/plain"); draggedElement = document.getElementById(id); } else { draggedElement = currentDraggedElement; // 拖拽结束后重置全局变量 currentDraggedElement = null; } const container = event.target.closest(".droppable"); if (container && draggedElement) { const existingImage = container.querySelector("img"); if (existingImage) { // 修复原代码bug:找到现有元素的原父容器,而非仅第一个可拖拽容器 const originalDraggableContainer = existingImage.closest(".draggable"); container.removeChild(existingImage); originalDraggableContainer.appendChild(existingImage); } container.appendChild(draggedElement); draggedElement.classList.remove('hide'); } }
3. 修正Touch事件绑定逻辑
原代码给droppable元素绑定touchstart是错误的,需调整事件绑定位置,确保拖拽过程的状态跟踪正确:
// 重新绑定拖拽元素的Touch事件 draggableElements.forEach(elem => { elem.addEventListener("dragstart", dragStart); elem.addEventListener("touchstart", dragStart); // 在拖拽元素上绑定touchmove,跟踪拖拽过程 elem.addEventListener("touchmove", (e) => { e.preventDefault(); // 阻止默认滚动行为 }); }); // 调整droppable元素的Touch事件绑定 droppableElements.forEach(elem => { elem.addEventListener("dragenter", dragEnter); elem.addEventListener("dragover", dragOver ); elem.addEventListener("dragleave", dragLeave); elem.addEventListener("drop", drop); // 用touchmove判断是否进入droppable区域 elem.addEventListener("touchmove", (e) => { e.preventDefault(); dragOver(e); }); elem.addEventListener("touchleave", dragLeave); }); // 在document上绑定全局触摸结束事件,确保拖拽结束时触发处理逻辑 document.addEventListener("touchend", drop); document.addEventListener("touchcancel", () => { if (currentDraggedElement) { currentDraggedElement.classList.remove('hide'); currentDraggedElement = null; } });
4. 新增移动端拖拽视觉反馈(可选但推荐)
移动端触摸拖拽没有默认视觉跟随效果,手动添加镜像元素提升交互体验:
function dragStart(event) { if (event.type === 'dragstart') { event.dataTransfer.setData('text/plain', event.target.id); } else { currentDraggedElement = event.target; event.preventDefault(); // 创建拖拽镜像元素 const dragImage = currentDraggedElement.cloneNode(true); dragImage.style.position = 'absolute'; dragImage.style.zIndex = '9999'; dragImage.style.opacity = '0.8'; document.body.appendChild(dragImage); // 存储镜像元素,用于touchmove时更新位置 currentDraggedElement.dragImage = dragImage; // 设置镜像元素初始位置 const touch = event.touches[0]; dragImage.style.left = `${touch.clientX - dragImage.offsetWidth/2}px`; dragImage.style.top = `${touch.clientY - dragImage.offsetHeight/2}px`; } setTimeout(() => { event.target.classList.add('hide'); }, 0); } // 在touchmove中更新镜像元素位置 draggableElements.forEach(elem => { elem.addEventListener("touchmove", (e) => { e.preventDefault(); if (!currentDraggedElement?.dragImage) return; const touch = e.touches[0]; currentDraggedElement.dragImage.style.left = `${touch.clientX - currentDraggedElement.dragImage.offsetWidth/2}px`; currentDraggedElement.dragImage.style.top = `${touch.clientY - currentDraggedElement.dragImage.offsetHeight/2}px`; }); }); // 在drop和touchcancel中移除镜像元素 function drop(event) { // ... 原有逻辑 ... // 清理镜像元素 if (currentDraggedElement?.dragImage) { document.body.removeChild(currentDraggedElement.dragImage); delete currentDraggedElement.dragImage; } } document.addEventListener("touchcancel", () => { if (currentDraggedElement) { currentDraggedElement.classList.remove('hide'); if (currentDraggedElement.dragImage) { document.body.removeChild(currentDraggedElement.dragImage); delete currentDraggedElement.dragImage; } currentDraggedElement = null; } });
5. 补充CSS兼容性设置
添加以下CSS确保移动端触摸交互正常:
.draggable { touch-action: none; /* 阻止浏览器默认触摸行为(如双击缩放) */ user-select: none; /* 禁止文本选中干扰拖拽 */ } .droppable { touch-action: none; }
内容的提问来源于stack exchange,提问作者Manar Mani
相关产品推荐
相关产品推荐

