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 获取对象 基本选择与层级
May 31 Javascript
js multiple全选与取消全选实现代码
Dec 04 Javascript
左右悬浮可分组的网站QQ在线客服代码(可谓经典)
Dec 21 Javascript
js获取某月的最后一天日期的简单实例
Jun 22 Javascript
javascript中parseInt()函数的定义和用法分析
Dec 20 Javascript
JavaScript编写点击查看大图的页面半透明遮罩层效果实例
May 09 Javascript
Javascript基础知识盲点总结之函数
May 15 Javascript
AngularJS ng-repeat数组有重复值的解决方法
Oct 23 Javascript
浅谈js继承的实现及公有、私有、静态方法的书写
Oct 28 Javascript
微信小程序页面开发注意事项整理
May 18 Javascript
解决vue中post方式提交数据后台无法接收的问题
Aug 11 Javascript
es6 super关键字的理解与应用实例分析
Feb 15 Javascript
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
php 日期和时间的处理-郑阿奇(续)
2011/07/04 PHP
thinkPHP框架实现类似java过滤器的简单方法示例
2018/09/05 PHP
深入理解Javascript中的循环优化
2013/11/09 Javascript
jquery 操作iframe的几种方法总结
2013/12/13 Javascript
浏览器缩放检测的js代码
2014/09/28 Javascript
jQuery.holdReady()方法用法实例
2014/12/27 Javascript
jQuery中children()方法用法实例
2015/01/07 Javascript
jQuery简单实现禁用右键菜单
2015/03/10 Javascript
详谈javascript异步编程
2016/02/21 Javascript
JS实现将数字金额转换为大写人民币汉字的方法
2016/08/02 Javascript
解决Window10系统下Node安装报错的问题分析
2016/12/13 Javascript
js原生Ajax的封装和原理详解
2017/03/11 Javascript
JavaScript中使用import 和require打包后实现原理分析
2018/03/07 Javascript
javascript性能优化之分时函数的介绍
2018/03/28 Javascript
vue-cli脚手架搭建的项目去除eslint验证的方法
2018/09/29 Javascript
js中对象和面向对象与Json介绍
2019/01/21 Javascript
微信小程序自定义组件传值 页面和组件相互传数据操作示例
2019/05/05 Javascript
vue store之状态管理模式的详细介绍
2019/06/13 Javascript
[00:43]魔廷新尊——痛苦女王至宝捆绑包
2020/06/12 DOTA
[59:15]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第一场 11.20
2020/11/20 DOTA
对Python 数组的切片操作详解
2018/07/02 Python
python opencv实现切变换 不裁减图片
2018/07/26 Python
Python3模拟curl发送post请求操作示例
2019/05/03 Python
python实现图片中文字分割效果
2019/07/22 Python
python psutil模块使用方法解析
2019/08/01 Python
Python实现链表反转的方法分析【迭代法与递归法】
2020/02/22 Python
python sklearn包——混淆矩阵、分类报告等自动生成方式
2020/02/28 Python
为世界各地的女性设计和生产时尚服装:ROMWE
2016/09/17 全球购物
英国护发和美妆在线商店:Klip Shop
2019/03/24 全球购物
Booking.com缤客中国:全球酒店在线预订网站
2020/05/03 全球购物
信息系统专业个人求职信范文
2013/12/07 职场文书
校庆活动策划方案
2014/06/05 职场文书
学校交通安全责任书
2014/08/25 职场文书
先进教师个人事迹材料
2014/12/15 职场文书
争做文明公民倡议书
2019/06/24 职场文书
2021好看的国漫排行榜前十名 《完美世界》上榜,《元龙》排名第一
2022/03/18 国漫