拖拽复位后Div意外跳至鼠标位置的问题排查与修复咨询
拖拽复位后Div意外跳至鼠标位置的问题排查与修复咨询
我想在HTML中模拟手机端拖拽删除/复位的交互效果:用户拖拽蓝色的time_slot Div,若拖拽距离未达屏幕宽度的75%,Div会通过动画复位到初始位置;若超过则删除Div。目前大部分逻辑正常,但Div完成复位动画后,会突然跳转到当前鼠标位置,即使isDown已经在mouseup事件中设为false。
问题复现代码
HTML
<body style="margin: 0;"> <div id="BUS_auto_refresh_setting_area" class="BUS_auto_refresh_setting_area_style"> <div id="BUS_auto_refresh_setting_sub_area" class="BUS_auto_refresh_setting_sub_area_style"> <div style="width: 100%; height: 100%; border: 2px solid red;"> <div id="BUS_auto_refresh_setting_header_region" > <div style="font-size: 18px; text-align: center;" class="prevent-select"> This is a test.</div> </div> <div id="time_slot_region" class="time_slot_style"> </div> </div> </div> </div> </body>
CSS
* { box-sizing: border-box; font-family: Calibri; } #BUS_auto_refresh_setting_sub_area::-webkit-scrollbar { display: none; } #BUS_auto_refresh_setting_sub_area { -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } .time_slot { margin-left: 0px; width: 80%; height: 50px; background-color: #0077ff; border: 2px solid grey; border-radius: 15px; } .prevent-select { -webkit-user-select: none; -ms-user-select: none; user-select: none; } .BUS_auto_refresh_setting_area_style { display: flex; flex-direction: row; justify-content: center; width: 100%; height: 100%; top:0px; margin-right:auto; margin-left:auto; overflow: auto; background-color: rgb(0,0,0);background-color: rgba(0,0,0,0.6); z-index: 6; overflow-x: hidden; } .BUS_auto_refresh_setting_sub_area_style { display: flex; flex-direction: row; width: 80%; height: 100%; position: relative; background-color: #fefefe; } .time_slot_style { display: flex; flex-direction: column; justify-content: flex-start; align-items: center; }
JavaScript
var w = window.innerWidth; var h = window.innerHeight; document.getElementById("BUS_auto_refresh_setting_header_region").style.height = h * 0.1 + "px"; document.getElementById("BUS_auto_refresh_setting_header_region").style.top = 0 + "px"; document.getElementById("BUS_auto_refresh_setting_area").style.height = h + "px"; function create_a_new_time_slot_div() { var mousePosition; var offset = [0,0]; var div; var isDown = false; var is_mousse_click_before = false; var x_displacement; div = document.createElement("div"); div.style.position = "absolute"; //div.style.left = "0px"; var base_height = document.getElementById("BUS_auto_refresh_setting_header_region").style.height.substring(0, document.getElementById("BUS_auto_refresh_setting_header_region").style.height.length - 2); div.style.top = (document.getElementById("time_slot_region").childNodes.length) * base_height + "px"; //console.log(base_height); var id_name = "time_slot_" + (document.getElementById("time_slot_region").childNodes.length); div.id = id_name; div.style.left = "0px"; div.setAttribute("class", "time_slot"); document.getElementById("time_slot_region").appendChild(div); div.addEventListener('mousedown', function(e) { isDown = true; offset = [ div.offsetLeft - e.clientX, div.offsetTop - e.clientY ]; document.addEventListener('mousemove', function(event) { console.log(isDown); //event.preventDefault(); if (isDown) { console.log("Moving"); mousePosition = { x : event.clientX, y : event.clientY }; x_displacement = (mousePosition.x + offset[0]) console.log("x_displacement is " + x_displacement); div.style.left = x_displacement + 'px'; } }, true); }, true); document.addEventListener('mouseup', function(event) { isDown = false; mouseFinalPosition = { x : event.clientX, y : event.clientY }; if (w * -0.75 < x_displacement && x_displacement < w * 0.75) { console.log("Return"); var loading = document.getElementById(id_name); var keyFrames = document.createElement("style"); keyFrames.innerHTML = `@keyframes another_loading { from { left: ` + x_displacement + `px; } to { left: 0px; } } .time_slot { animation: another_loading 0.5s 1 linear forwards; }`; loading.appendChild(keyFrames); } else { div.remove(); } }, true); document.getElementById(id_name).addEventListener("animationend", function() { document.getElementById(id_name).removeChild(document.getElementById(id_name).lastElementChild); console.log("Now cancelled"); }); } create_a_new_time_slot_div();
问题根源分析
事件绑定与移除不匹配:你在
mousedown中给document添加了匿名的mousemove事件监听器,但后续尝试移除时,又创建了一个新的匿名函数——匿名函数无法被正确移除(内存中是两个不同的函数引用),导致mousemove事件一直存在。当动画结束后,若鼠标仍在移动,可能会触发事件并覆盖动画设置的样式。动画与内联样式的冲突:动画通过
forwards关键字会把left:0px应用为内联样式,但如果残留的mousemove事件触发,会直接覆盖这个内联样式,导致Div跳走。事件队列延迟触发:浏览器的事件队列可能存在延迟,
mouseup设置isDown=false后,仍有未处理的mousemove事件在队列中,动画结束后触发就会导致异常。
修复方案
1. 命名mousemove事件处理函数,确保能正确移除
把mousemove的处理函数抽成命名函数,这样在mouseup时可以准确移除:
function create_a_new_time_slot_div() { // ... 其他变量定义 ... var moveHandler; // 声明命名的移动处理函数 div.addEventListener('mousedown', function(e) { e.preventDefault(); // 阻止默认行为,避免文本选择干扰 isDown = true; offset = [ div.offsetLeft - e.clientX, div.offsetTop - e.clientY ]; // 命名函数,方便后续移除 moveHandler = function(event) { if (isDown) { mousePosition = { x : event.clientX, y : event.clientY }; x_displacement = (mousePosition.x + offset[0]); div.style.left = x_displacement + 'px'; } }; document.addEventListener('mousemove', moveHandler, true); }, true); document.addEventListener('mouseup', function(event) { isDown = false; // 立即移除mousemove事件,避免后续触发 document.removeEventListener('mousemove', moveHandler, true); mouseFinalPosition = { x : event.clientX, y : event.clientY }; if (w * -0.75 < x_displacement && x_displacement < w * 0.75) { // ... 原有动画代码不变 ... } else { div.remove(); } }, true); // ... 其他代码 ... }
2. 动画结束后强制同步内联样式
在animationend事件中手动设置left:0px,确保样式稳定:
document.getElementById(id_name).addEventListener("animationend", function() { document.getElementById(id_name).removeChild(document.getElementById(id_name).lastElementChild); // 强制设置left为0,避免样式冲突 this.style.left = "0px"; console.log("Now cancelled"); });
3. 优化动画选择器,避免全局影响
原有动画用了.time_slot类选择器,会影响所有同类型Div,建议改为针对当前Div的ID选择器:
keyFrames.innerHTML = `@keyframes another_loading_${id_name} { from { left: ` + x_displacement + `px; } to { left: 0px; } } #${id_name} { animation: another_loading_${id_name} 0.5s 1 linear forwards; }`;
备注:内容来源于stack exchange,提问作者BikeProgram
相关产品推荐
相关产品推荐

