JavaScript CSS修改学习第六章 拖拽


Posted in Javascript onFebruary 19, 2010

当示例的box上的#链接处于活动状态的时候(不论是用tab然后点击enter或者使用鼠标点击)这个元素就能够通过方向键拖拽。然后点击enter或者Esc释放。(可以随意改变这些键。我不确定释放键应该设置成为什么所以enter和Esc都可以)

 

使用

1、复制文章后面的dragDrop对象。

2、复制我的addEventSimple和removeEventSimple函数,这里需要。

3、设定keyHTML和keySpeed属性(下面有解释)。

4、确定你所要拖拽的元素都有位置属性:absolute或者fixed。

5、把所有可拖拽的元素发送到对象的initElement函数。可以发送一个对象或者对象ID的字符串。例如:

dragDrop.initElement('test');
dragDrop.initElement(document.getElementById('test2'));

6、当元素被拖拽过后,代码会自动添加dragged类。你可以添加一些CSS效果。

7、如果你想当用户放开元素之后做一些事情,你可以给releaseElement添加自己的函数。

属性

你需要设置两个属性。

keyHTML包含一个需要拖拽的元素的键盘能访问到的链接的内容。为了保持HTML简洁,这里只添加一个有简单样式的类。你可以随意构建你的HTML,但是要记住一点就是必须有一个链接让键盘能够访问到,键盘用户需要一个焦点来触发拖拽事件。

keySpeed用来设置键盘拖拽的速度,每次按键移动多少像素。我喜欢设置为10,你也可以尝试一下其他的值。

这里还有7个属性,但是都是在代码内部的。初始化的时候都设置为undefined,然后相应的函数会设置他们。

 

拖拽对象


复制下面这个对象到你的页面,不要忘了addEventSimple和removeEventSimple。

dragDrop = { 
    keyHTML: '<a href="#" class="keyLink">#</a>', 
    keySpeed: 10, // pixels per keypress event 
    initialMouseX: undefined, 
    initialMouseY: undefined, 
    startX: undefined, 
    startY: undefined, 
    dXKeys: undefined, 
    dYKeys: undefined, 
    draggedObject: undefined, 
    initElement: function (element) { 
        if (typeof element == 'string') 
            element = document.getElementById(element); 
        element.onmousedown = dragDrop.startDragMouse; 
        element.innerHTML += dragDrop.keyHTML; 
        var links = element.getElementsByTagName('a'); 
        var lastLink = links[links.length-1]; 
        lastLink.relatedElement = element; 
        lastLink.onclick = dragDrop.startDragKeys; 
    }, 
    startDragMouse: function (e) { 
        dragDrop.startDrag(this); 
        var evt = e || window.event; 
        dragDrop.initialMouseX = evt.clientX; 
        dragDrop.initialMouseY = evt.clientY; 
        addEventSimple(document,'mousemove',dragDrop.dragMouse); 
        addEventSimple(document,'mouseup',dragDrop.releaseElement); 
        return false; 
    }, 
    startDragKeys: function () { 
        dragDrop.startDrag(this.relatedElement); 
        dragDrop.dXKeys = dragDrop.dYKeys = 0; 
        addEventSimple(document,'keydown',dragDrop.dragKeys); 
        addEventSimple(document,'keypress',dragDrop.switchKeyEvents); 
        this.blur(); 
        return false; 
    }, 
    startDrag: function (obj) { 
        if (dragDrop.draggedObject) 
            dragDrop.releaseElement(); 
        dragDrop.startX = obj.offsetLeft; 
        dragDrop.startY = obj.offsetTop; 
        dragDrop.draggedObject = obj; 
        obj.className += ' dragged'; 
    }, 
    dragMouse: function (e) { 
        var evt = e || window.event; 
        var dX = evt.clientX - dragDrop.initialMouseX; 
        var dY = evt.clientY - dragDrop.initialMouseY; 
        dragDrop.setPosition(dX,dY); 
        return false; 
    }, 
    dragKeys: function(e) { 
        var evt = e || window.event; 
        var key = evt.keyCode; 
        switch (key) { 
            case 37:    // left 
            case 63234: 
                dragDrop.dXKeys -= dragDrop.keySpeed; 
                break; 
            case 38:    // up 
            case 63232: 
                dragDrop.dYKeys -= dragDrop.keySpeed; 
                break; 
            case 39:    // right 
            case 63235: 
                dragDrop.dXKeys += dragDrop.keySpeed; 
                break; 
            case 40:    // down 
            case 63233: 
                dragDrop.dYKeys += dragDrop.keySpeed; 
                break; 
            case 13:     // enter 
            case 27:     // escape 
                dragDrop.releaseElement(); 
                return false; 
            default: 
                return true; 
        } 
        dragDrop.setPosition(dragDrop.dXKeys,dragDrop.dYKeys); 
        if (evt.preventDefault) 
            evt.preventDefault(); 
        return false; 
    }, 
    setPosition: function (dx,dy) { 
        dragDrop.draggedObject.style.left = dragDrop.startX + dx + 'px'; 
        dragDrop.draggedObject.style.top = dragDrop.startY + dy + 'px'; 
    }, 
    switchKeyEvents: function () { 
        // for Opera and Safari 1.3 
        removeEventSimple(document,'keydown',dragDrop.dragKeys); 
        removeEventSimple(document,'keypress',dragDrop.switchKeyEvents); 
        addEventSimple(document,'keypress',dragDrop.dragKeys); 
    }, 
    releaseElement: function() { 
        removeEventSimple(document,'mousemove',dragDrop.dragMouse); 
        removeEventSimple(document,'mouseup',dragDrop.releaseElement); 
        removeEventSimple(document,'keypress',dragDrop.dragKeys); 
        removeEventSimple(document,'keypress',dragDrop.switchKeyEvents); 
        removeEventSimple(document,'keydown',dragDrop.dragKeys); 
        dragDrop.draggedObject.className = dragDrop.draggedObject.className.replace(/dragged/,''); 
        dragDrop.draggedObject = null; 
    } 
}

拖拽是什么
拖拽是在屏幕上移动元素的一种方法。为了让元素能够移动,元素必须有position属性:absolute或者fixed,这样才能通过修改它的坐标(style.top和style.left)让它移动。
(理论上position:relative也可以,但是几乎没用。另外,那样需要额外的数据来计算,这里我没有写)
设置坐标很简单;找到需要设置的元素的坐标是这个代码比较难的部分。大多数代码都是用来处理这个问题的。
另外,保持易用性也比较重要。传统上通过鼠标来拖拽一个元素是最好的办法,但是也要考虑到没有鼠标的用户,所以也要保证键盘的可用性。

基础知识
让我们先来看看一些基础知识
初始化一个元素
每个拖拽代码都从初始化元素开始。这个工作通过下面的函完成:

initElement: function (element) { 
    if (typeof element == 'string') 
        element = document.getElementById(element); 
    element.onmousedown = dragDrop.startDragMouse; 
    element.innerHTML += dragDrop.keyHTML; 
    var links = element.getElementsByTagName('a'); 
    var lastLink = links[links.length-1]; 
    lastLink.relatedElement = element; 
    lastLink.onclick = dragDrop.startDragKeys; 
},

如果函数接收到一个字符串,那么就会当做元素ID来处理。然后给这个元素设置一个onmousedown事件,用来开始鼠标部分的代码。注意这里我使用的是传统事件注册方式;因为我希望this关键字能够在startDragDrop里起作用。

然后把用户定义的keyHTML添加到元素上,我相信这个链接是用来触发键盘事件的。然后为这个链接设置键盘的触发程序。然后存储主元素在relatedElement里面,我们后面需要。

现在代码就等用户动作了

基本位置信息
我打算使用下面的方法来:首先我会读取拖拽元素的初始位置,保存在startX和startY里面。然后计算鼠标移动的位置或者键盘控制下移动的位置来决定元素从初始位置移动的范围。
JavaScript CSS修改学习第六章 拖拽 
startX和startY通过startDrag函数来设置,这个函数在鼠标和键盘事件里都会用到。

startDrag: function (obj) { 
    if (dragDrop.draggedObject) 
        dragDrop.releaseElement(); 
    dragDrop.startX = obj.offsetLeft; 
    dragDrop.startY = obj.offsetTop; 
    dragDrop.draggedObject = obj; 
    obj.className += ' dragged'; 
},

首先,如果元素处于拖拽状态,那么我们就释放他(我们后面再讲)。
然后函数会找到元素在起始位置的坐标(offsetLeft和offsetTop),然后保存在startX和startY以备后用。
然后在draggedObject里面保存一个对象的引用。然后给他添加dragged类,这样就可以通过CSS来设置拖拽时候的样式了
当用户使用鼠标或者键盘拖拽元素的时候,代码的最复杂的部分就要跟踪位置的变化。然后给出dX和dY(X和Y的变化)。然后加上startX和startY就是元素现在的位置。
下面的函数用来设定位置:
setPosition: function (dx,dy) { 
    dragDrop.draggedObject.style.left = dragDrop.startX + dx + 'px'; 
    dragDrop.draggedObject.style.top = dragDrop.startY + dy + 'px'; 
},

函数通过鼠标和键盘的移动计算所得的dX和dY与初始位置,来设定元素的新位置。
这部分很简单,复杂的地方就在于dx和dy的获得,鼠标部分和键盘部分的处理非常的不同,我们分别来看。

鼠标部分的代码
鼠标部分的计算方面比键盘的要复杂一些,但是在浏览器兼容性方面问题不大。所以我们从鼠标部分开始。
事件
首先我们来讨论事件。很明显的,在拖拽过程中需要mousedown,mousemove,mouseup用来完成选择对象,拖拽,拖拽完成这几个动作。
这一系列事件从需要拖拽元素的mousedown事件开始。所以所有的拖拽元素都需要这个事件来标明拖拽开始了。我们看到:

element.onmousedown = dragDrop.startDragMouse;

然而mousemove和mouseup事件不应该设置在元素上而应该设置在整个文档上。因为用户可能很快很疯狂的移动鼠标,然后丢失拖拽元素。如果设置在元素上,因为鼠标不在元素上了所以可能会无法控制,这对于易用性来说不是好事。
如果我们再文档上设置mousemove和mouseup,就没有这个问题。不管鼠标在哪,元素都会响应mousemove和mouseup。这个的易用性就很强。
另外你只能在拖拽开始以后再设置mousemove和mouseup,然后当用户释放元素之后删除它们。这样代码很干净而且节省系统资源,因为mousemove对系统的消耗很大。
Mousedown
当拖拽元素发生mousedown事件的时候,startDragMouse函数就开始执行:

首先会执行我们之前讨论过的startDrag。然后查找鼠标的坐标然后保存在initialMouseX和initialMouseY中。后面我们会把鼠标位置跟这个比较。

最后会返回false,这个用来阻止默认鼠标事件:选择文本。我们不想再拖拽的时候有文本被选中,这很烦人。

JavaScript CSS修改学习第六章 拖拽

然后给文档设置mouseup和mousemove事件处理程序。因为有可能文档有他自己的mouseup和mousemove事件处理程序,所以我使用我的addEventSimple函数防止原来的事件处理程序失效。
Mousemove
现在,当用户移动鼠标的时候dragMouse函数就执行了。

dragMouse: function (e) { 
    var evt = e || window.event; 
    var dX = evt.clientX - dragDrop.initialMouseX; 
    var dY = evt.clientY - dragDrop.initialMouseY; 
    dragDrop.setPosition(dX,dY); 
    return false; 
},

这个函数会读取鼠标现在的坐标,然后减去之前的坐标,把得到的dX和dY传递给sePosition。

然后通过返回false来阻止鼠标选择文本的默认属性。

JavaScript CSS修改学习第六章 拖拽

Mouseup

当用户松开鼠标的时候,会调用releaseElement。我们后面讨论。

 

键盘部分代码

现在我们开始更复杂的键盘部分代码。不像鼠标拖拽那样,键盘拖拽并没有一个标准。虽说基本的交互不是太复杂,但是最好还是简要说明一下。

基本交互

用来拖拽的键最好是方向键,这很简单。

激活和释放元素是比较有技巧的,在这里我的代码还需要加强。

我觉得如果用键盘来激活的话就应该使用一个我添加的额外的链接。这里没有太多选择:因为链接能够在所有的浏览器里面获得焦点(好吧,表单也可以,你也可是选择复选框),而且把一个链接放置在可拖拽的元素里面也是合乎逻辑的(你可以放在任何地方,但是如何让用户知道那个是用来激活拖拽的呢?)。

我假设当用户点击enter或者Esc的时候释放元素,至少我没找到其他合适的键。你想选择其他的话可以在这里查找键盘代码。

case 13: // enter
case 27: // escape
	dragDrop.releaseElement();
	return false;

 

事件

点击可以激活元素。当鼠标点击链接或者当元素获得焦点的时候点击enter键就能激活。所以键盘代码的激活可以使点击enter键或者点击链接。

(严格来说,当你用鼠标点击链接的时候,元素先被鼠标事件激活然后释放了然后再被键盘模式激活。)

事件的其余部分也非常的模糊。当你想检测方向键的时候键盘事件尤为麻烦。

首先我们需要一个允许重复点击的事件,因为用户可能按着方向键不放,那么事件就需要一遍遍的触发,这样拖拽才能继续。所以我们使用keypress事件。

不幸的是,IE在keypress的情况下不支持方向键。在IE里面keydown会重复发生,看起来我们需要使用keydown事件了。

你可能才到事情没那么简单。在Opera和Safari里面keydown事件只能触发一次,所以当用户按下键之后,元素移动一次之后就不动了。在这些浏览器中我们需要keypress。

所以理想情况下,我们使用keypress,如果不支持就是用keydown。但是怎么切换事件呢?你又怎么知道keypress在这个时候不能用呢?

我的解决办法就是给keypress事件设置一个事件处理程序。如果这个程序执行了说明支持keypress,我们就可以安全的切换了。

startDragKeys函数用来设置keydown和keypress事件:

addEventSimple(document,'keydown',dragDrop.dragKeys); 
addEventSimple(document,'keypress',dragDrop.switchKeyEvents);

首先keydown触发完成拖拽的dragKeys函数。这是第一个触发的事件,而且元素总会移动。然后我们做其他的话,那么元素在Opera和Safari1.3里面移动一次以后就会停止。
这就是为什么我们还需要keypress。第一个keypress事件会触发switchKeyEvents函数,这个函数会调整事件处理程序:
switchKeyEvents: function () { 
    removeEventSimple(document,'keydown',dragDrop.dragKeys); 
    removeEventSimple(document,'keypress',dragDrop.switchKeyEvents); 
    addEventSimple(document,'keypress',dragDrop.dragKeys); 
},

他会先删除掉原来的事件处理程序,然后将keypress设置为触发dragKeys。因为这个函数只会在支持他的浏览器里面执行,所以我们只在这些浏览器里面将keydown改为keypress。
初始键盘代码
当用户点击了连接激活了元素,那么就会调用startDragKeys。
startDragKeys: function () { 
    dragDrop.startDrag(this.relatedElement); 
    dragDrop.dXKeys = dragDrop.dYKeys = 0; 
    addEventSimple(document,'keydown',dragDrop.dragKeys); 
    addEventSimple(document,'keypress',dragDrop.switchKeyEvents); 
    this.blur(); 
    return false; 
},

首先会调用我们之前讨论过的startDrag函数。他会给这个函数传递relatedElement,也就是要拖拽的元素。
然后将dXKeys和dYKeys设置为0。这些变量用来跟踪元素的位移。
然后设置事件处理程序,上面已经讨论过了。
然后移除刚才点击的链接的焦点。我这样做是因为Enter键会释放元素,但是如果不移除焦点,当用户点击了Enter键之后,元素被释放,但是链接却再次被Enter点击,又成了可拖动的模式。如果我们移除焦点,那么问题就不存在了。
最后返回false来阻止默认动作。
通过键盘拖拽
dragKeys负责键盘拖拽:
dragKeys: function(e) { 
    var evt = e || window.event; 
    var key = evt.keyCode;

我们首先读取键盘的键值。
然后我们使用switch语句来决定我们怎么做。这部分的目的是更新dXKeys和dYKeys的值,就可以通过设置元素的位置来移动元素了。
switch (key) { 
    case 37:    // left 
    case 63234: 
        dragDrop.dXKeys -= dragDrop.keySpeed; 
        break; 
    case 38:    // up 
    case 63232: 
        dragDrop.dYKeys -= dragDrop.keySpeed; 
        break; 
    case 39:    // right 
    case 63235: 
        dragDrop.dXKeys += dragDrop.keySpeed; 
        break; 
    case 40:    // down 
    case 63233: 
        dragDrop.dYKeys += dragDrop.keySpeed; 
        break;

作者通过设置keySpeed来确定每次移动的像素大小。当用户点击左方向键,就减去keySpeed。
JavaScript CSS修改学习第六章 拖拽 
这个代码包含63232-63235的情况。因为Safari1.3没有使用标准的37-40的方向键的键值(Safari 3已经支持了)。
case 13: // enter 
case 27: // escape 
dragDrop.releaseElement(); 
return false;

如果用户点击Enter或者Esc键,就调用releaseElement()函数。如果你想改变释放元素的按键,可以再这里添加。
default: 
return true; 
}

如果用户按下了其他键,就执行默认动作并且结束函数。
dragDrop.setPosition(dragDrop.dXKeys,dragDrop.dYKeys);

现在dXKeys和dYKeys已经更新我们发送到setPosition()函数中来改变元素的位置。
if (evt.preventDefault) 
evt.preventDefault(); 
return false; 
},

最后我们需要阻止默认事件,如果用户点击下方向键,那么在执行完上面的代码后页面会向下滚动。W3C兼容浏览器中通过preventDefault实现,在IE中通过返回false实现。

释放元素
当用户释放了元素,函数releaseElement就会被调用。他会移除所有代码设置的事件处理程序,移除dragged类,清理draggedObject然后等待用户动作。

releaseElement: function() { 
    removeEventSimple(document,'mousemove',dragDrop.dragMouse); 
    removeEventSimple(document,'mouseup',dragDrop.releaseElement); 
    removeEventSimple(document,'keypress',dragDrop.dragKeys); 
    removeEventSimple(document,'keypress',dragDrop.switchKeyEvents); 
    removeEventSimple(document,'keydown',dragDrop.dragKeys); 
    dragDrop.draggedObject.className = dragDrop.draggedObject.className.replace(/dragged/,''); 
    dragDrop.draggedObject = null; 
}

你或许在用户释放元素之后还想做些什么,可以把你的函数添加在这里。
翻译地址:http://www.quirksmode.org/js/dragdrop.html
转载请保留以下信息
作者:北玉(tw:@rehawk)
Javascript 相关文章推荐
Jquery 常用方法经典总结
Jan 28 Javascript
JavaScript Distilled 基础知识与函数
Apr 07 Javascript
js跨浏览器实现将字符串转化为xml对象的方法
Sep 25 Javascript
在JS中解析HTML字符串示例代码
Apr 16 Javascript
实例剖析AngularJS框架中数据的双向绑定运用
Mar 04 Javascript
使用jQuery操作HTML的table表格的实例解析
Mar 13 Javascript
fullpage.js全屏滚动插件使用实例
Sep 06 Javascript
深入理解vue路由的使用
Mar 24 Javascript
Vue中的v-for循环key属性注意事项小结
Aug 12 Javascript
npm的lock机制解析
Jun 20 Javascript
详解Vue Cli浏览器兼容性实践
Jun 08 Javascript
vue如何使用模拟的json数据查看效果
Mar 31 Vue.js
JavaScript CSS修改学习第五章 给“上传”添加样式
Feb 19 #Javascript
JavaScript CSS 修改学习第四章 透明度设置
Feb 19 #Javascript
JavaScript CSS修改学习第三章 修改样式表
Feb 19 #Javascript
JavaScript CSS修改学习第二章 样式
Feb 19 #Javascript
JavaScript CSS修改学习第一章 查找位置
Feb 19 #Javascript
JavaScript DOM 学习第九章 选取范围的介绍
Feb 19 #Javascript
JavaScript DOM学习第八章 表单错误提示
Feb 19 #Javascript
You might like
用文本文件制作留言板提示(上)
2006/10/09 PHP
PHP中实现中文字符进制转换原理分析
2011/12/06 PHP
ZF框架实现发送邮件的方法
2015/12/03 PHP
php数据库的增删改查 php与javascript之间的交互
2017/08/31 PHP
Flash+XML滚动新闻代码 无图片 附源码下载
2007/11/22 Javascript
JS+CSS设置img在DIV中只显示Img垂直居中的部分
2013/10/24 Javascript
jQuery中:nth-child选择器用法实例
2014/12/31 Javascript
基于Jquery easyui 选中特定的tab
2015/11/17 Javascript
浅谈js中调用函数时加不加括号的问题
2016/07/28 Javascript
如何检测JavaScript的各种类型
2016/07/30 Javascript
浅谈jquery采用attr修改form表单enctype不起作用的问题
2016/11/25 Javascript
分享bootstrap学习笔记心得(组件及其属性)
2017/01/11 Javascript
JavaScript正则表达式简单实用实例
2017/06/23 Javascript
Ionic3实现图片瀑布流布局
2017/08/09 Javascript
NodeJS收发GET和POST请求的示例代码
2017/08/25 NodeJs
在vue使用clipboard.js进行一键复制文本的实现示例
2019/01/15 Javascript
jquery实现的分页显示功能示例
2019/08/23 jQuery
解决Vue项目打包后打开index.html页面显示空白以及图片路径错误的问题
2019/10/25 Javascript
微信小程序登陆注册功能的实现代码
2019/12/10 Javascript
Python的Django框架使用入门指引
2015/04/15 Python
python排序函数sort()与sorted()的区别
2018/09/18 Python
在pycharm上mongodb配置及可视化设置方法
2018/11/30 Python
python识别图像并提取文字的实现方法
2019/06/28 Python
通过Python编写一个简单登录功能过程解析
2019/09/04 Python
css3动画事件—webkitAnimationEnd与计时器time事件
2013/01/31 HTML / CSS
巴西最好的男鞋:Rafarillo
2018/05/25 全球购物
Viking Direct爱尔兰:办公用品和家具
2019/11/21 全球购物
怎样声明一个匿名的内部类
2016/06/01 面试题
测量工程专业求职信
2014/02/24 职场文书
加入学生会演讲稿
2014/04/24 职场文书
有关环保的标语
2014/06/13 职场文书
法院四风对照检查材料思想汇报
2014/10/06 职场文书
庆六一宣传标语
2014/10/08 职场文书
党员群众路线个人整改措施思想汇报
2014/10/12 职场文书
捐助倡议书
2015/01/19 职场文书
Mysql中 unique列插入重复值该怎么解决呢
2021/05/26 MySQL