You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

移动端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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 13:42:53