移动端e.target表现异常?拖拽功能调试问题求助
移动端调试时e.target识别父级div而非子元素的问题
开发者工具切换到移动端屏幕模式时,e.target会识别父级div(尤其是flex布局元素),而非可见可点击的子元素;但电脑屏幕模式下表现正常。这种情况是否正常?有没有解决方法?
HTML代码
<div class="top"> <div class="list-container clicked__clue" id="books"> <ul class="list"> <li class="list-item book_4" draggable="true"> <p class="list-item-name">기이현상청 사건일지</p> </li> <li class="list-item book_1" draggable="true"> <p class="list-item-name">맨 끝줄 소년</p> </li> <li class="list-item book_5" draggable="true"> <p class="list-item-name">억지로라도 쉬어가라</p> </li> <li class="list-item book_3" draggable="true"> <p class="list-item-name">로펌 인 코리아</p> </li> <li class="list-item book_7" draggable="true"> <p class="list-item-name">거미 여인의 키스</p> </li> <li class="list-item book_6" draggable="true"> <p class="list-item-name">을사늑약 1905</p> </li> <li class="list-item book_9" draggable="true"> <p class="list-item-name">다른 방식으로 보기</p> </li> <li class="list-item book_2" draggable="true"> <p class="list-item-name">발이 없는 나의 여인은 노래한다</p> </li> <li class="list-item book_8" draggable="true"> <p class="list-item-name">닐과 순다리</p> </li> </ul> </div> </div>
CSS代码
.list-container { background: #f9e2c3; position: relative; margin: auto; } .list { list-style-type: none; margin: 0; padding: 1rem; font-size: 1.5rem; } .list-item { display: flex; float: left; align-self: baseline; padding: 1rem; text-orientation: upright; writing-mode: vertical-lr; } .list-item-name { font-family: "Consolas"; user-select: none; }
JavaScript代码
const list = document.querySelector(".list"); let draggedTarget; let helper; document.addEventListener("dragstart", function(e) { draggedTarget = e.target; helper = document.createElement("div"); helper.innerText = draggedTarget.querySelector(".list-item-name").innerText; helper.style.position = "absolute"; helper.style.top = "-9999px"; helper.style.padding = "1rem"; helper.style.backgroundColor = "#000"; helper.style.color = "#ddd"; helper.style.fontSize = "1.5rem"; helper.style.fontFamily = "Consolas"; document.querySelector(".top").appendChild(helper); e.dataTransfer.setDragImage(helper, 0, -30); }); document.addEventListener("dragenter", function(e) { if (e.target !== draggedTarget && e.target.classList[0] === "list-item") { const ep = e.target.previousElementSibling; const en = e.target.nextElementSibling; const dp = draggedTarget.previousElementSibling; const dn = draggedTarget.nextElementSibling; if (!ep && !dn) { list.removeChild(draggedTarget); e.target.insertAdjacentElement("beforebegin", draggedTarget); } else if (!en && !dp) { list.removeChild(draggedTarget); e.target.insertAdjacentElement("afterend", draggedTarget); } else if (ep && ep != draggedTarget) { list.removeChild(e.target); list.removeChild(draggedTarget); ep.insertAdjacentElement("afterend", draggedTarget); draggedTarget.insertAdjacentElement("afterend", e.target); } else if (!ep) { list.removeChild(e.target); list.removeChild(draggedTarget); dn.insertAdjacentElement("beforebegin", e.target); e.target.insertAdjacentElement("beforebegin", draggedTarget); } else if (en && en != draggedTarget) { list.removeChild(e.target); list.removeChild(draggedTarget); en.insertAdjacentElement("beforebegin", draggedTarget); draggedTarget.insertAdjacentElement("beforebegin", e.target); } else if (!en) { list.removeChild(e.target); dp.insertAdjacentElement("afterend", e.target); } } }); document.addEventListener("dragover", function(e) { e.preventDefault(); // why necessary ? }); document.addEventListener("drop", function(e) { e.preventDefault(); helper.parentNode.removeChild(helper); });
问题解析与解决方法
为什么会出现这种情况?
这是正常现象,核心原因是移动端触摸事件和桌面端鼠标事件的命中逻辑差异:
- 桌面端鼠标是单点精准触发,
e.target严格对应指针点击的元素。 - 移动端触摸事件(包括开发者工具模拟的触摸)的命中区域计算更宽泛,当子元素尺寸小、布局存在间隙,或者父级是flex容器(占据空间更大)时,浏览器会优先把事件绑定到父级元素。
- 另外你给
list-item同时用了float: left和display: flex,这种混合布局会干扰盒模型计算,进一步放大事件命中的偏差。
可行的解决方法
1. 修复布局冲突
移除list-item的float: left,flex布局不需要浮动,浮动会打乱flex的正常排列逻辑,也会影响事件命中:
.list-item { display: flex; /* float: left; 删掉这行 */ align-self: baseline; padding: 1rem; text-orientation: upright; writing-mode: vertical-lr; }
2. 用closest()精确获取目标元素
在事件处理函数中,通过closest('.list-item')向上查找最近的目标子元素,不管事件触发在父级还是子级,都能拿到正确的列表项:
修改JS中的事件逻辑:
// dragstart事件里 draggedTarget = e.target.closest('.list-item'); // dragenter事件里 const targetItem = e.target.closest('.list-item'); if (targetItem && targetItem !== draggedTarget) { // 后续所有用到e.target的地方换成targetItem const ep = targetItem.previousElementSibling; const en = targetItem.nextElementSibling; // ... 其余逻辑保持不变 }
3. 让子元素填满父级点击区域
给list-item-name设置宽高100%,避免点击到父级的空白区域:
.list-item-name { font-family: "Consolas"; user-select: none; width: 100%; height: 100%; }
4. 优化触摸事件响应(可选)
如果是真实移动端设备的问题,添加touch-action: manipulation可以禁用浏览器的触摸延迟和默认行为,提升事件命中精度:
.list-item { /* 其他样式 */ touch-action: manipulation; }
内容的提问来源于stack exchange,提问作者Eunbin
相关产品推荐
相关产品推荐

