
JS做DOM节点实时拖动拖拽的一些坑
大坑:监听DOM不对,导致拖动会失效。
相关事件:onmousedown、onmousedown、onmousemove
例如,有DOM对象demo,我们要实现demo的拖动,就需要对它监听onmousedown事件,用于触发拖动的开始。
var demo = document.getElementById('#demo'); demo.addEventListener('mousedown',function(e) { console.log('成功监听demo的鼠标按下事件') });
嗯,这样自然就死监听成功了,很多朋友都会继续给demo监听onmousemove和onmouseup事件啦。理论上来说,监听继续给demo监听这两个事件是没问题的,但是我们在实际操作中会发现,demo移动着突然移动不了。而且,明显能感觉demo与鼠标的移动直接有延迟。没错,就是这个延迟导致了移动突然中止。因为延迟的时候,鼠标已经移出了demo的范围,导致onmousemove事件监听中断。*PS:没有屏蔽延迟的方法,延迟是事件的冒泡与捕获产生,自然就不能真正意义上的实时监听到对应事件。
解决方法其实就很简单了,我们拖动的demo是放在document中的,我们监听document的onmousemove事件就能避开范围移除问题(如果移出了浏览器范围,就是硬伤了)。
var demo = document.getElementById('#demo'); demo.addEventListener('mousedown',function(e) { console.log('成功监听demo的鼠标按下事件'); }); document.addEventListener('mousemove',function(e) { console.log('正在拖动','X:',e.clientX); console.log('正在拖动','Y:',e.clientY); // 我们通过监听e.clientX和e.clientY做demo的移动 }); document.addEventListener('mouseup',function(e) { console.log('鼠标松开啦'); });
但是有些朋友会发现,监听到document上的话,demo就不听话了,所以需要对他们加一个状态,或者通过添加事件移除事件来对demo的拖动进行监听。
// 方案一 var demo = document.getElementById('#demo'); var move_status = false; demo.addEventListener('mousedown',function(e) { console.log('成功监听demo的鼠标按下事件'); move_status = true; }); document.addEventListener('mousemove',function(e) { if (!move_status) return; console.log('正在拖动','X:',e.clientX); console.log('正在拖动','Y:',e.clientY); // 我们通过监听e.clientX和e.clientY做demo的移动 }); document.addEventListener('mouseup',function(e) { move_status = false; console.log('鼠标松开啦'); // 我们在这里处理最后的计算,然后将demo放到对应位置 }); // 方案二 var demo = document.getElementById('#demo'); // 拖动处理方法 var move_handler = function(e) { console.log('正在拖动','X:',e.clientX); console.log('正在拖动','Y:',e.clientY); // 我们通过监听e.clientX和e.clientY做demo的移动 } // 拖动结束处理方法 var move_end = function(e) { console.log('鼠标松开啦'); // 我们在这里处理最后的计算,然后将demo放到对应位置 document.removeEventListener('mousemove',move_handler); document.removeEventListener('mouseup',move_end); } // 监听鼠标按下触发拖动 demo.addEventListener('mousedown',function(e) { console.log('成功监听demo的鼠标按下事件'); document.addEventListener('mousemove',move_handler); document.addEventListener('mouseup',move_end); });
解决这些问题以后,大家在拖动的时候,还会发现,拖动的时候,demo中的文本与其它节点文本会被选中,很影响我们操作体验。解决方法很取巧,就是在拖动开始的时候,给全局加上一些css内容,防止文本被选中。
.none-select { -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; }
当然,这上面都是我为文章总结出来的代码案例,并非实际项目中的代码。实际项目中,我们可以借助jQuery完成更好的dom操作管理。在实际的使用中,我们需要写更完善的方法,下面我贴出一段项目代码,因为代码是项目中的代码片段,不能直接拿来用哦。
/** * moveContentItemWatch - 移动文章节点 */ XXXXXX.prototype.moveContentItemWatch = function(selector) { var _this = this; var activeMoveWatch; $(document).on('mousedown',selector,function(e) { _this.data._startX = e.clientX; _this.data._startY = e.clientY; _this.data._moveItem = $(this); _this.data._moveParentNode = $(this).parent(); _this.data._moveSelector = selector; activeMoveWatch = setTimeout(function() { if (_this.status.editing) return; _this.status.moveItem = true; },300); }).on('mousemove',function(e) { if (!_this.status.moveItem) return; _this.data._moveX = e.clientX; _this.data._moveY = e.clientY; _this.handlerItemMove('move'); }).on('mouseup',function(e) { clearTimeout(activeMoveWatch); if (_this.status.moveItem) { _this.handlerItemMove('end'); } }); return this; }