在实际应用拖拽的时候发现自己犯了两个错误,都是当文档内容多余一个屏幕的时候才会出现。
错误一,绝对位置
刚开始非常不解为什么拖拽需要获得鼠标相对文档的绝对位置,我之前写的代码也都是仅仅使用clientX和clientY实现的,这两个属性是相对浏览器的,没有将文档的滚动计算在内。因此在内容超过一页的时候,如果用户使用鼠标拖拽元素,不会有问题。但是如果用户采用边拖拽边滚滚动条的方式就会造成文档滚动部分没有被计算在内。测试了一下,豆瓣、百度空间、MSN Space的拖拽都不会有这个问题。
总之,应该使用元素相对文档的绝对位置。
错误二,没有更新鼠标位置和元素位置
下面是我刚开始想出来的拖拽实现方法,不过有一个严重的问题,就是在鼠标移动的时候不会动态地更新鼠标当前位置以及元素当前位置。但是如果我们使用相对位置的时候这个问题不会得到暴露。
- 拖拽状态 = 0
- 鼠标在元素上按下的时候{
- 拖拽状态 = 1
- 记录下鼠标的x和y坐标
- 记录下元素的x和y坐标
- }
- 鼠标在元素上移动的时候{
- 如果拖拽状态是0就什么也不做。
- 如果拖拽状态是1,那么
- 元素y = 现在鼠标y - 原来鼠标y + 原来元素y
- 元素x = 现在鼠标x - 原来鼠标x + 原来元素x
- }
- 鼠标在任何时候放开的时候{
- 拖拽状态 = 0
- }
拖拽状态 = 0
鼠标在元素上按下的时候{
拖拽状态 = 1
记录下鼠标的x和y坐标
记录下元素的x和y坐标
}
鼠标在元素上移动的时候{
如果拖拽状态是0就什么也不做。
如果拖拽状态是1,那么
元素y = 现在鼠标y - 原来鼠标y + 原来元素y
元素x = 现在鼠标x - 原来鼠标x + 原来元素x
}
鼠标在任何时候放开的时候{
拖拽状态 = 0
}
应该修改为:
- 拖拽状态 = 0
- 鼠标在元素上按下的时候{
- 拖拽状态 = 1
- 记录下鼠标的x和y坐标
- 记录下元素的x和y坐标
- }
- 鼠标在元素上移动的时候{
- 如果拖拽状态是0就什么也不做。
- 如果拖拽状态是1,那么
- 元素y = 现在鼠标y - 原来鼠标y + 原来元素y
- 元素x = 现在鼠标x - 原来鼠标x + 原来元素x
- 更新元素坐标的记录!
- 更新鼠标坐标的记录!
- }
- 鼠标在任何时候放开的时候{
- 拖拽状态 = 0
- }
拖拽状态 = 0
鼠标在元素上按下的时候{
拖拽状态 = 1
记录下鼠标的x和y坐标
记录下元素的x和y坐标
}
鼠标在元素上移动的时候{
如果拖拽状态是0就什么也不做。
如果拖拽状态是1,那么
元素y = 现在鼠标y - 原来鼠标y + 原来元素y
元素x = 现在鼠标x - 原来鼠标x + 原来元素x
更新元素坐标的记录!
更新鼠标坐标的记录!
}
鼠标在任何时候放开的时候{
拖拽状态 = 0
}
找到问题所在之后,代码的修改就比较简单了。最后的代码如下:
- <script type="text/javascript">
- //记录当前的拖拽元素
- var dragElement = null;
- //4个全局变量,用来记录当前拖拽元素以及鼠标的坐标
- //由于不可能同时拖拽多个元素,所以不会冲突
- var mouseY,mouseX,objY,objX;
- //用于动态更新z-index,确保不会造成不该移动的元素瞎动。
- var max = 1;
- //初始化网页,遍历所有的元素,如果元素的class属性为"drag"
- //则对其进行适当地设置
- function dragInit(node){
- if(node.className == "drag"){
- node.onmousedown = down;
- document.onmousemove = move;
- node.onmouseover = over;
- node.style.position = "relative";
- node.dragging = false;
- }
- var children = node.childNodes;
- for(var i = 0;i < children.length; i++){
- dragInit(children[i]);
- }
- }
- window.onload = function(){
- //从document开始遍历
- dragInit(document);
- //确保在文档的任何位置将鼠标松开都将导致拖拽停止
- document.onmouseup = docUp;
- window.onblur = docUp;
- }
- //鼠标按下响应事件
- function down(event)
- {
- event = event || window.event;
- //按谁拽谁
- dragElement = this;
- //记录鼠标的当前坐标
- mouseX = parseInt(event.clientX)
- +(document.documentElement.scrollLeft || document.body.scrollLeft);
- mouseY = parseInt(event.clientY)
- +(document.documentElement.scrollTop || document.body.scrollTop);
- //记录元素的当前坐标
- objY = parseInt(getNodeStyle(dragElement,"top"));
- objX = parseInt(getNodeStyle(dragElement,"left"));
- //IE不返回未设置的CSS属性
- if(!objY)objY=0;
- if(!objX)objX=0;
- this.style.zIndex = max++;
- }
- function move(event){
- event = event || window.event;
- if(dragElement){
- var x,y;
- //y等于鼠标当前y - 记录的鼠标y + 元素y ,后面的x一样
- y = parseInt(event.clientY)
- +(document.documentElement.scrollTop || document.body.scrollTop)
- - mouseY + objY;
- x = parseInt(event.clientX)
- +(document.documentElement.scrollLeft || document.body.scrollLeft)
- - mouseX + objX;
- //更新元素的实际显示
- dragElement.style.top = y + "px";
- dragElement.style.left = x + "px";
- //更新鼠标坐标和元素坐标的记录
- objY=y;
- objX=x;
- mouseX = parseInt(event.clientX)
- +(document.documentElement.scrollLeft || document.body.scrollLeft);
- mouseY = parseInt(event.clientY)+(
- document.documentElement.scrollTop || document.body.scrollTop);
- }
- }
- function docUp(){
- //停止拖拽
- dragElement = null;
- }
- function over(){
- this.style.cursor = "move";
- }
- //获得元素的坐标
- function getNodeStyle(node,styleName){
- var realStyle = null;
- if(node.currentStyle){
- realStyle = node.currentStyle[styleName];
- }else if(window.getComputedStyle){
- realStyle = window.getComputedStyle(node,null)[styleName];
- }
- return realStyle;
- }
- </script>
<script type="text/javascript">
//记录当前的拖拽元素
var dragElement = null;
//4个全局变量,用来记录当前拖拽元素以及鼠标的坐标
//由于不可能同时拖拽多个元素,所以不会冲突
var mouseY,mouseX,objY,objX;
//用于动态更新z-index,确保不会造成不该移动的元素瞎动。
var max = 1;
//初始化网页,遍历所有的元素,如果元素的class属性为"drag"
//则对其进行适当地设置
function dragInit(node){
if(node.className == "drag"){
node.onmousedown = down;
document.onmousemove = move;
node.onmouseover = over;
node.style.position = "relative";
node.dragging = false;
}
var children = node.childNodes;
for(var i = 0;i < children.length; i++){
dragInit(children[i]);
}
}
window.onload = function(){
//从document开始遍历
dragInit(document);
//确保在文档的任何位置将鼠标松开都将导致拖拽停止
document.onmouseup = docUp;
window.onblur = docUp;
}
//鼠标按下响应事件
function down(event)
{
event = event || window.event;
//按谁拽谁
dragElement = this;
//记录鼠标的当前坐标
mouseX = parseInt(event.clientX)
+(document.documentElement.scrollLeft || document.body.scrollLeft);
mouseY = parseInt(event.clientY)
+(document.documentElement.scrollTop || document.body.scrollTop);
//记录元素的当前坐标
objY = parseInt(getNodeStyle(dragElement,"top"));
objX = parseInt(getNodeStyle(dragElement,"left"));
//IE不返回未设置的CSS属性
if(!objY)objY=0;
if(!objX)objX=0;
this.style.zIndex = max++;
}
function move(event){
event = event || window.event;
if(dragElement){
var x,y;
//y等于鼠标当前y - 记录的鼠标y + 元素y ,后面的x一样
y = parseInt(event.clientY)
+(document.documentElement.scrollTop || document.body.scrollTop)
- mouseY + objY;
x = parseInt(event.clientX)
+(document.documentElement.scrollLeft || document.body.scrollLeft)
- mouseX + objX;
//更新元素的实际显示
dragElement.style.top = y + "px";
dragElement.style.left = x + "px";
//更新鼠标坐标和元素坐标的记录
objY=y;
objX=x;
mouseX = parseInt(event.clientX)
+(document.documentElement.scrollLeft || document.body.scrollLeft);
mouseY = parseInt(event.clientY)+(
document.documentElement.scrollTop || document.body.scrollTop);
}
}
function docUp(){
//停止拖拽
dragElement = null;
}
function over(){
this.style.cursor = "move";
}
//获得元素的坐标
function getNodeStyle(node,styleName){
var realStyle = null;
if(node.currentStyle){
realStyle = node.currentStyle[styleName];
}else if(window.getComputedStyle){
realStyle = window.getComputedStyle(node,null)[styleName];
}
return realStyle;
}
</script>
点击进入测试页面。
JavaScript拖拽系列
1. JavaScript拖拽
2. JavaScript拖拽2——多元素、分离JS
3. JavaScript拖拽3——解决快速拖拽的问题
4. JavaScript拖拽4——获得元素的位置
5. JavaScript拖拽5——性能优化
6. JavaScript拖拽6——修复错误