可拖动的DIV
在做WEB UI设计的时候,拖动某个HTML元素已经成为一种不能忽视的用户界面模式,比较典型的应用例子就是Dialog,一个元素是怎么实现拖动的呢?其实原理非常简单,要想实现首先得了解几个基本知识。
Tips
绝对定位:只有把元素的position属性设置为absolute并且或者fixed才可以实现拖动,默认情况下元素会按文档流中的位置自行决定其出现在页面上的位置,是不能移动的,而绝对定位的元素可以使元素脱离文档流,相对于其定位的父元素或者屏幕定位,可以利用这点儿,通过改变元素与已定位父元素的位移来实现元素拖动。关于定位知识具体可以看看CSS布局 ——从display,position, float属性谈起。
鼠标事件:当鼠标按下、移动、弹起的时候都会触发相应事件,当鼠标按下的时候同时会触发相应元素click事件,并且冒泡到document,上面提到改变元素与定位父容器位移可以在这些事件中实现。关于事件相关知识可以看看JavaScript与HTML交互——事件
要拖动的Dialog
写个简易的Dialog供拖动测试使用
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style type="text/css" >
html,body
{
height:100%;
width:100%;
padding:0;
margin:0;
} .dialog
{
width:250px;
height:250px;
position:absolute;
background-color:#ccc;
-webkit-box-shadow:1px 1px 3px #292929;
-moz-box-shadow:1px 1px 3px #292929;
box-shadow:1px 1px 3px #292929;
margin:10px;
} .dialog-title
{
color:#fff;
background-color:#404040;
font-size:12pt;
font-weight:bold;
padding:4px 6px;
cursor:move;
} .dialog-content
{
padding:4px;
}
</style>
</head>
<body>
<div id="dlgTest" class="dialog">
<div class="dialog-title">Dialog</div>
<div class="dialog-content">
This is a draggable test.
</div>
</div>
</body>
</html>
看起来是酱紫的

拖动一下
为了简单,这里就不照顾浏览器兼容性问题了,先基于Chrome实现。上面的Dialog定位夫容器为document,鼠标event对象包含clientX和clientY两个属性,标识鼠标当前相对ViewPort(可视窗口)位置,可以在移动的时候改变Dialog的left和top属性值实现其移动。
var isDialogTitle=false;
function down(e){
if(e.target.className.indexOf('dialog-title')!=-1){
isDialogTitle=true;
}
}
function move(e){
var dialog=document.getElementById('dlgTest');
if(isDialogTitle){//只有点击Dialog Title的时候才能拖动
dialog.style.left=e.clientX+'px';
dialog.style.top=e.clientY+'px';
}
}
function up(e){
isDialogTitle=false;
}
document.addEventListener('mousedown',down);
document.addEventListener('mousemove',move);
document.addEventListener('mouseup',up);
这样拖动效果就实现了,为了确保只有鼠标点击Dialog Title的时候才拖动,当鼠标按下的时候要判断事件源,如果是Dialog Title区域的话,把isDialogTitle标记设为true,鼠标移动的时候首先要判断isDialogTitle,在鼠标弹起的时候将标记设为false。
一跳一跳的
亲自试过demo的同学肯定可以当开始移动的时候Dialog会跳一下,这是怎么个情况?仔细看看代码发现在移动初始,代码就把Dialog的left和top设为了鼠标当前位置,可是用户在拖动的时候不会刻意去点Dialog的左上角,这样就跳了,soga!改进一下
var draggingObj=null; //dragging Dialog
var diffX=0;
var diffY=0; function down(e){
if(e.target.className.indexOf('dialog-title')!=-1){
draggingObj=e.target.offsetParent;
diffX=event.clientX-draggingObj.offsetLeft;
diffY=event.clientY-draggingObj.offsetTop;
}
} function move(e){
var dialog=document.getElementById('dlgTest');
if(draggingObj){//只有点击Dialog Title的时候才能拖动
dialog.style.left=(e.clientX-diffX)+'px';
dialog.style.top=(e.clientY-diffY)+'px';
}
} function up(e){
draggingObj=null;
diffX=0;
diffY=0;
} document.addEventListener('mousedown',down);
document.addEventListener('mousemove',move);
document.addEventListener('mouseup',up);
好赤果果
经过改动后不再跳跃了,但是很暴露的感觉,最开始定义的三个变量都暴露在window下,而且这种写法相当的没有通用性,万一以后Dialog Title变了呢,凡是用过此方法的地方都得改一遍,万一Title内部还有子元素,点击其子元素的时候怎么办?既然如此,穿件衣服封装一下
var Dragging=function(validateHandler){ //参数为验证点击区域是否为可移动区域,如果是返回欲移动元素,负责返回null
var draggingObj=null; //dragging Dialog
var diffX=0;
var diffY=0;
function mouseHandler(e){
switch(e.type){
case 'mousedown':
draggingObj=validateHandler(e);//验证是否为可点击移动区域
if(draggingObj!=null){
diffX=e.clientX-draggingObj.offsetLeft;
diffY=e.clientY-draggingObj.offsetTop;
}
break;
case 'mousemove':
if(draggingObj){
draggingObj.style.left=(e.clientX-diffX)+'px';
draggingObj.style.top=(e.clientY-diffY)+'px';
}
break;
case 'mouseup':
draggingObj =null;
diffX=0;
diffY=0;
break;
}
};
return {
enable:function(){
document.addEventListener('mousedown',mouseHandler);
document.addEventListener('mousemove',mouseHandler);
document.addEventListener('mouseup',mouseHandler);
},
disable:function(){
document.removeEventListener('mousedown',mouseHandler);
document.removeEventListener('mousemove',mouseHandler);
document.removeEventListener('mouseup',mouseHandler);
}
}
}
包装一下果真变好看多了,代码不难看懂,有几个注意点,Dragging函数的validateHandler参数并不是什么阿猫阿狗,正如注释所言为了解决刚才提到几个需求变更问题,validateHandler是一个自定义函数的句柄,这个函数用于识别点击元素是否触发移动,是的话需要返回欲移动元素,这样就可以灵活的触发移动并决定移动那个元素了(点击的和移动的不一定是一个),Dragging函数返回一个对象,对象中有两个方法,分别可以使元素可移动/禁止移动,看看怎么使用
function getDraggingDialog(e){
var target=e.target;
while(target && target.className.indexOf('dialog-title')==-1){
target=target.offsetParent;
}
if(target!=null){
return target.offsetParent;
}else{
return null;
}
}
Dragging(getDraggingDialog).enable();
首先定义一个识别函数,然后作为参数调用Dragging函数,并调用返回值的enable方法,这样元素就可以拖动了。
源码
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style type="text/css" >
html,body
{
height:100%;
width:100%;
padding:0;
margin:0;
} .dialog
{
width:250px;
height:250px;
position:absolute;
background-color:#ccc;
-webkit-box-shadow:1px 1px 3px #292929;
-moz-box-shadow:1px 1px 3px #292929;
box-shadow:1px 1px 3px #292929;
margin:10px;
} .dialog-title
{
color:#fff;
background-color:#404040;
font-size:12pt;
font-weight:bold;
padding:4px 6px;
cursor:move;
} .dialog-content
{
padding:4px;
}
</style>
</head>
<body>
<div id="dlgTest" class="dialog">
<div class="dialog-title">Dialog</div>
<div class="dialog-content">
This is a draggable test.
</div>
</div>
<script type="text/javascript">
var Dragging=function(validateHandler){ //参数为验证点击区域是否为可移动区域,如果是返回欲移动元素,负责返回null
var draggingObj=null; //dragging Dialog
var diffX=0;
var diffY=0; function mouseHandler(e){
switch(e.type){
case 'mousedown':
draggingObj=validateHandler(e);//验证是否为可点击移动区域
if(draggingObj!=null){
diffX=e.clientX-draggingObj.offsetLeft;
diffY=e.clientY-draggingObj.offsetTop;
}
break; case 'mousemove':
if(draggingObj){
draggingObj.style.left=(e.clientX-diffX)+'px';
draggingObj.style.top=(e.clientY-diffY)+'px';
}
break; case 'mouseup':
draggingObj =null;
diffX=0;
diffY=0;
break;
}
}; return {
enable:function(){
document.addEventListener('mousedown',mouseHandler);
document.addEventListener('mousemove',mouseHandler);
document.addEventListener('mouseup',mouseHandler);
},
disable:function(){
document.removeEventListener('mousedown',mouseHandler);
document.removeEventListener('mousemove',mouseHandler);
document.removeEventListener('mouseup',mouseHandler);
}
}
} function getDraggingDialog(e){
var target=e.target;
while(target && target.className.indexOf('dialog-title')==-1){
target=target.offsetParent;
}
if(target!=null){
return target.offsetParent;
}else{
return null;
}
} Dragging(getDraggingDialog).enable();
</script>
</body>
</html>
不足之处
这种拖动处理方式看起来不错了,但是还有几点儿遗憾
1. 前面提到的浏览器兼容性问题,这种写法在低版本IE浏览器上是不能运行的
2. 边界检查,细心的同学发现Dialog不但可以拖动了,还可以使页面出现滚动条无限拖动,大部分情况下我们希望Dialog在可视窗口、文档(固有滚动条内)或者固定区域内拖动,这种方式没有做到此限制
3. 拖动卡顿,在这个demo中不会出现此问题,文档结构简单拖动流畅,可视在庞大的页面中如果鼠标移动速度过快,Dialog会跟不上鼠标,出现卡顿,这时候如果鼠标在Dialog外面,mouseup事件不会生效,拖动就停不下来,只能把鼠标移回Dialog在mouseup
前两个问题好解决,拓展一下模块就可以,至于第三个现在还没想到比较好的解决办法,十一点了,明天再研究研究,然后一块儿发出来,晚安。
可拖动的DIV的更多相关文章
- jQuery实现鼠标拖动改变Div高度
最近项目中需要在DashBoard页面做一个事件通知栏,该通知栏固定位于页面底部,鼠标拖动该DIV实现自动改变高度扩展内容显示区域. 以下是一个设计原型,基于jQuery实现,只实现了拖动效果,没有做 ...
- 可拖动的DIV续
之前写过一篇可拖动的DIV讲如何实现可拖动的元素,最后提出了几点不足,这篇文章主要就是回答着三个问题 1. 浏览器兼容性 2. 边界检查 3. 拖动卡顿.失灵 先附上上次代码 <!DOCTYPE ...
- 鼠标拖动改变DIV等网页元素的大小的最佳实践
1.初次实现 1.1 html代码 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" la ...
- jquery 拖动改变div大小
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 可拖动的div——demo
可拖动的div——demo 我们经常会遇到这样的注册界面 我们以前经常可以遇到这种需要注册的网站,如上图: 上图有一个特点,即是上述注册框其实是一个div,同时可以拖动,以下做一个简单的实例,就可以实 ...
- 【转】弹出可拖动的DIV层提示窗口
来源:www.divcss5.com <html> <head> <meta http-equiv="Content-Type" content=&q ...
- 创建一个可拖动的DIV
var drag = function(){ var obj = document.getElementById("id"); var s = obj.style; var b = ...
- [置顶] 原创鼠标拖动实现DIV排序
先上效果图: 对比传统的排序,这是一个很不错的尝试,希望对大家有启发. 大家可以参考我的上一篇博文:http://blog.csdn.net/littlebo01/article/details/12 ...
- 如何用JavaScript做一个可拖动的div层
可拖动的层在Web设计中用处很多,比如在某些需要自定义风格布局的应用中,控件就需要拖动操作,下面介绍一个,希望可以满足你的需求,顺便学习一下可拖动的层是如何实现的. 下面是效果演示: 这个DIV可以移 ...
随机推荐
- 用Backbone.js教程系列的链接
整理了一下用Backbone.js系列教程链接. Backbone.js入门教程 用Backbone.js创建一个联系人管理系统(一) 用Backbone.js创建一个联系人管理系统(二) 用Back ...
- esri联邦用户大会 总结
1 概述 1.1 文档概述 ESRI联邦用户大会已于2月25日到2月27日在美国华盛顿举行,现就其会议中设计到的内容总结如下: 1.2 景观分析 这是第一个demo,演示的是"景观分析&qu ...
- java:关于继承变量的值问题
1.在java中,如果子类继承父类的静态变量时,当你在子类面前修改这个静态变量的值,其父类的静态变量也会改变. 案例: //父类public class Animal { //静态属性 public ...
- c#数据库访问读取数据速度测试
1,使用byte数据读取 2,使用dataset数据读取
- SpringMVC常用注解實例詳解1:@Controller,@RequestMapping,@RequestParam,@PathVariable
我的開發環境 框架: springmvc+spring+freemarker 開發工具: springsource-tool-suite-2.9.0 JDK版本: 1.6.0_29 to ...
- JS自执行匿名函数
常见格式:(function() { /* code */ })(); 解释:包围函数(function(){})的第一对括号向脚本返回未命名的函数,随后一对空括号立即执行返回的未命名函数,括号内为匿 ...
- winAPI 中 的 GlobalLock GlobalUnlock 的作用
在项目中遇到GlobalLock GlobalUnlock 这两个操作内存的函数. 百度百科解释为:锁定内存中指定的内存块,并返回一个地址值,令其指向内存块的起始处.除非用 GlobalUnlock ...
- middleware中间件的概念
简要来说express就是一个由路由和中间件构成的 web 开发框架. 当express服务收到客户端的请求时,会通过一组函数来处理请求. 这些函数用于解析请求体数据,处理错误,或者负责返回各种情况对 ...
- iOS项目旋转控制
iOS6的旋屏控制技巧 在iOS5.1 和 之前的版本中, 我们通常利用 shouldAutorotateToInterfaceOrientation: 来单独控制某个UIViewController ...
- Testng之使用@DataProvider注解做数据驱动【转】
原文:http://www.jianshu.com/p/8e333a0ec42a 前两天学了一下@DataProvider,今天有时间总结一下.testng很强大,提供了很多注解,其中利用@DataP ...