JavaScript拖放(Drag and Drop)

代码/示例drag and drop

其实,这篇主要讲的还是事件,不过是围绕“拖放”这个功能来讲。

整个拖放的逻辑如下:

document.onmousedown = dragBegin;//鼠标按下,拖放开始
document.onmousemove = dragMove;//鼠标移动,进行拖放
document.onmouseup = dragEnd;//鼠标松开,拖放结束
function dragBegin(e){
	dragging = true;//开始
	//记录鼠标的当前坐标
	//记录元素的当前坐标
}
function dragMove(e){
	if(dragging){
	//元素的新位置 = 元素旧位置 + 鼠标新位置 - 鼠标旧位置
	}
}
function dragEnd(e){
	dragging = false;//结束
}

要实现拖放,就必须弄清楚鼠标的位置、拖放对象的位置,然后根据鼠标移动后的相对位置,对拖放对象的位置进行修改。

 

1、鼠标位置

鼠标位置是从event对象中取得的,一共有6对鼠标坐标属性,其参考点与兼容性见下表:

属性对 参考点 兼容性
clientX,clientY 浏览器窗口 所有,除了Safari
layerX, layerY 最近的绝对定位的父节点 Mozilla、Safari
offsetX, offsetY 事件目标对象 所有,除了Mozilla
pageX, pageY document对象 所有,除了IE
screenX, screenY 计算机屏幕 所有
x, y 各个浏览器不同 互不兼容

screenX, screenY虽然完全兼容,但是我们一般比较少用到相对于屏幕的位置,一般比较常用的是以document为参考点的鼠标位置,最好的便是pageX与pageY了,但是IE竟然不支持!恨呀~

但是,好在我们可以用clientX、clientY加上页面的滚动偏移量,在IE中得到pageX、pageY的值:

//获取鼠标位置,返回{x,y}
function getMouseXY(e){
	var x=0, y=0;
	if(e.pageX||e.pageY){
		x = e.pageX;
		y = e.pageY;
	}else if(e.clientX||e.clientY){
		x = e.clientX +
			document.documentElement.scrollLeft +
			document.body.scrollLeft;
		y = e.clientY +
			document.documentElement.scrollTop +
			document.body.scrollTop;
	}
	return {x:x,y:y};
}

在拖放的过程中,如果我们不是按照document来定位,直接用clientXY,就可能出现滚动鼠标滚轮,页面下翻,然后元素没有跟着下移的情况。

 

2、事件注册

我是将拖放事件全都注册到document上的,为什么要这样呢?

如果是注册到拖放对象上,我们考虑这样一种情况,当拖拽开始后,我滚动鼠标滚轮,此时页面下滑,但是由于没有触发mousemove事件,拖放对象并不会跟着移动,但是鼠标已经离开了拖放对象,拖放就失效了(拖拽速度过快也可能产生这种问题)

如果是注册在一个高层的元素上(不一定要是document),就算鼠标移出了拖拽对象,但是拖拽依旧正常进行,只要鼠标不移出那个高层的元素。

并且注册在document还有个好处,就是我可以针对符合某种条件的元素进行事件处理,而不必逐一进行事件绑定。

例如范例中,只要class属性中包含“dropObj”的元素,即可进行拖拽:

if(target.className.indexOf("dragObj")>=0){
	dragging = true;
	dragObj = target;//将事件的目标对象设为拖拽对象
	//do something
}

 

3、获取拖拽对象位置

在drogMove方法里面,我们计算元素的新位置用的是这样的公式:

元素的新位置 = 元素旧位置 + 鼠标新位置 – 鼠标旧位置

我们鼠标的位置已经获取到了,那元素的位置如何获得?

一个元素的样式可以写在很多个地方,包括style标签中,css文件中,还有直接写在标签上,但我们要获得的是元素的真正位置。

Microsoft的解决方法是currentStyle属性(只读的),使用方法和style属性相似。

W3C的方法是window.getComputedStyle(),该方法有两个参数,第一个是期望被计算样式的元素,第二个参数是任何期望的CSS伪对象(这个我也不是很理解,一般就是null,而且第二个参数是不能被忽略的)。

综合一下,诞生如下兼容方法:

function getRealStyle(node,styleName){
	var realStyle = null;
	if(node.currentStyle){
		realStyle = node.currentStyle[styleName];
	}else if(window.getComputedStyle){
		realStyle = window.getComputedStyle(node,null)[styleName];
	}
	return realStyle;
}

这个方法只能获得,那如何才能设置元素的位置呢?

一般这种经常变化的样式,不会写在css文件里面,直接写在元素的style属性(相当于标签的style属性)就好了。而且写在标签中的样式的权重是最高的,可以覆盖其他地方设置的样式。

dragObj.style.top = (divTop + mouseXY.y - startTop)+"px";
dragObj.style.left = (divLeft + mouseXY.x - startLeft)+"px";

(记得要加“px”)

 

4、优化

浏览器的mousemove事件的发生是很快的,1毫秒即可触发一次,如果频繁的调用这个事件,会产生一定的性能问题,我们可以通过下面这样的办法来解决:

//移动完之后,让浏览器休息会
document.onmousemove = null;
setTimeout("document.onmousemove = dragMove;",30);

原理就是这个事件触发之后将这个事件注销,等一段时间之后再绑定回去。这里我贪方便就直接用了事件注册的传统模型,更好的方法应该是用addEventSimple方法。

具体节约了多少资源,你可以打开资源管理器,狂拖乱拽一阵,看看浏览器进程占用的CUP飙到哪去了~~再拿没优化的版本来对比一下~~

(在我的机子上,没优化的版本狂拖乱拽的时候,Firefox进程占CUP30-40,优化后占20-30,还是提高了不少的)

 

我说:这个例子花了我不少心血呀~

发布者

Rolf

伪文艺IT攻城师,热爱前端,热爱互联。

《JavaScript拖放(Drag and Drop)》有2个想法

  1. Why are blankets with smallpox from 150+ years ago more siiinfncagt than the melamine and other toxic crap in our imported goods right now?How is from the other side of the globe and plopping them down in the most “hideously white” portions of middle America not genocide?

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

[喜欢] [嘻嘻] [奋斗] [问号] [鼓掌] [泪] [酷] [强] [耶] [握手] [心] [给力] [神马] [围观] [奥特曼] more »