H5拖放API基础篇

不要搞错,本文不是讲如何拖地的。看过《javascript精粹》朋友应该知道,他实现拖放的过程比较复杂,现在时代不同了,我们用H5的新的拖放API就能非常方便的实现拖放效果了。最近在园子见一园友写了一篇《HTML5 进阶系列:拖放 API 实现拖放排序》,真乃大师之作,大~熊同学作为一代菜鸟(不是宗师),无法与之想比,遂推出基础篇,希望各位园友有所收获。

一个简单的例子–地上有石子捡几个吧

    <title>地上有石子,捡几个吧</title>
    <meta charset="utf-8">
    <style type="text/css">
        .lanzi{
            width: 400px;
            height: 200px;
            border:1px solid black;
        }
        .floor{
            width: 500px;
            height: 80px;
            border: 1px solid black;
        }
        .shizi{
            display: inline-block;
            width: 40px;
            height: 40px;
            background: #ccc;
            border-radius: 20px;
            margin-top: 40px;
            text-align: center;
            line-height: 40px;
        }
    </style>
 
 
    <p>从前有个人走在丛林里,传来一个声音,说地上有石子,捡几个吧,会有用的。那个人不以为然,捡了一个,但是走出从林后发现手中的石子变成了金子...(
这个故事充满了哲学智慧),下面我们要给出一些石子,希望你能把它见到篮子里,没准编程金子哦!!
    </p>
    <h4>我是篮子</h4>   
    <div class="lanzi" id="lanzi"></div>
    <h4>我是地</h4>
    <div class="floor" id="floor">
        <span class="shizi" draggable="true" data-id="1">石子</span>
        <span class="shizi" draggable="true" data-id="2">石子</span>
        <span class="shizi" draggable="true" data-id="3">石子</span>
        <span class="shizi" draggable="true" data-id="4">石子</span>
        <span class="shizi" draggable="true" data-id="5">石子</span>
        <span class="shizi" draggable="true" data-id="6">石子</span>
        <span class="shizi" draggable="true" data-id="7">石子</span>
        <span class="shizi" draggable="true" data-id="8">石子</span>
        <span class="shizi" draggable="true" data-id="9">石子</span>
        <span class="shizi" draggable="true" data-id="10">石子</span>
    </div>
    <script type="text/javascript">
        window.addEventListener("load",handler,false);
        function handler(){
            var floor = document.getElementById("floor");
            var lanzi = document.getElementById("lanzi");
            var shizi;
            floor.addEventListener("dragstart",function(e){
                if(e.target.className == "shizi"){
                    shizi = e.target;
                    var dt = e.dataTransfer;
                    dt.effectedAllowed = "all";
                }
            },false);
 
            lanzi.addEventListener("dragend",function(e){e.preventDefault()},false);
            lanzi.addEventListener("drop",function(e){
                var data = e.dataTransfer.getData("text/plain");
                lanzi.appendChild(shizi);
                e.preventDefault();
                e.stopPropagation();
            },false);
 
            document.ondragover = function(e){e.preventDefault();}
            document.ondrop = function(e){e.preventDefault();}
        }
    </script>

(gif演示是用的edge,我的ubuntu做gif太麻烦了,借了个windows)
H5拖放API基础篇
这里用一个简单的例子演示了如何实现拖放,那么问题来了,从上面的演示中你可以猜出一些属性和方法的用法了,那些方法的作用究竟是怎样的?那些个属性又是啥意思呢?下面一一到来。

实现拖放的一般步骤

找到一个可拖的元素

正如不是所有人都叫大熊一样,并不是所有的元素都是可以被拖的。img和a元素默认可拖,其他元素默认不可拖,当时可以加一个draggable=true让它可拖。

<div draggable="true"></div>

处理拖放有关事件

所有相关事件如下:(这个摘自:http://www.cnblogs.com/linxin/p/6794542.html 或者 http://www.jqhtml.com/6273.html)

源对象:

  • dragstart:源对象开始拖放。
  • drag:源对象拖放过程中。
  • dragend:源对象拖放结束。

过程对象:

  • dragenter:源对象开始进入过程对象范围内。
  • dragover:源对象在过程对象范围内移动。
  • dragleave:源对象离开过程对象的范围。

目标对象:

  • drop:源对象被拖放到目标对象内。

我们可以用一个测试来看看这些事件的触发时机和事件对象为何物。

<meta charset="UTF-8">
<title>testEvents</title>
<style type="text/css">
    .source{
        width: 50px;
        height: 50px;
        border: 1px solid red;
    }
    .process{
        width: 100px;
        height: 100px;
        border: 1px solid black;
        margin-top: 10px;
    }
    .dest{
        width: 100px;
        height: 100px;
        border: 1px solid green;
        margin-top: 10px;
    }
</style>
 
 
<div class="source" id="source" draggable="true"></div>
<div class="process" id="process"></div>
<div class="dest" id="dest"></div>
<script type="text/javascript">
window.onload=function(){
    var source = document.getElementById("source");
    var process = document.getElementById("process");
    var dest = document.getElementById("dest");
    var sourceEle;
 
    source.addEventListener("dragstart",function(e){
        console.log("source dragstart");
        console.log(e);
        sourceEle = e.target;
        var dt = e.dataTransfer;
        dt.effectedAllowed = "all";
    },false);
 
    process.addEventListener("dragenter",function(e){
        console.log("process dragenter");
        console.log(e);
    },false);
 
    process.addEventListener("dragover",function(e){
        console.log("process dragover");
        console.log(e);
    },false);
 
    process.addEventListener("dragleave",function(e){
        console.log("process dragleave");
        console.log(e);
    },false);
 
    source.addEventListener("drag",function(e){
        console.log("source drag");
        console.log(e);
    },false);
 
    dest.addEventListener("dragend",function(e){
        console.log("dest dragend");
        console.log(e);
        e.preventDefault();
    },false);
 
    dest.addEventListener("drop",function(e){
        console.log("dest drop");
        console.log(e);
        dest.appendChild(sourceEle);
        e.preventDefault();
        e.stopPropagation();
    },false);
 
    document.ondragover = function(e){e.preventDefault();}
    document.ondrop = function(e){e.preventDefault();}
}
</script>

 这个例子将拖放过过程涉及的事件做了一个罗列,这里不再细讲,你可以查看控制台看看事件的触发顺序和事件对象。

一个重要的对象DataTransfer对象

这里首字母大写了,严格说叫做一个类,每一次拖放会实例化这个类,保存在事件对象的dataTransfer属性中。其属性和方法见下表(来自:http://www.cnblogs.com/ijjyo/p/4316232.html)
感谢这位兄台的总结,拿了你这么多东西,谢谢啊。
dataTransfer对象
成员表

属性 描述
dropEffect 设置或获取拖曳操作的类型和要显示的光标类型。
effectAllowed 设置或获取数据传送操作可应用于该对象的源元素。
方法 描述
clearData(sFormat) 通过 dataTransfer 对象从剪贴板删除一种或多种数据格式。
getData(sFormat) 通过 dataTransfer 对象从剪贴板获取指定格式的数据。
setData(sFormat,sData) 以指定格式给 dataTransfer 对象赋予数据。 sFormat: URL, Text
setDragImge(element, x, y) element:指定一个对象,当拖动发生的时候,显示在光标下方。x:x轴偏移量。y:y轴偏移量。

属性
这个两个属性对于初次接触DnD的朋友来说,可谓最令人摸不着头脑的,网上和各书籍上对这两个属性的解释均不全面,下面我试图尽量把它们讲明白。

effectAllowed 和 dropEffect 最主要的作用是,用于配置拖拽操作过程中鼠标指针的类型以便提示用户后续可执行怎样的操作;其次的作用是,控制 drop 事件的触发与否。

dropEffect
dropEffect

作用:用于设置目标元素将执行的操作,若属性值属于 effectAllowed 范围内,则鼠标指针将显示对应的指针样式,否则则显示禁止的指针样式。

取值范围:

copy :被拖拽元素将被复制到目标元素内,若属于 effectAllowed 范围内时,则鼠标指针显示复制的样式,否则则显示禁止的指针样式。

  link :被拖拽元素将以超链接的形式打开资源,若属于 effectAllowed 范围内时,则鼠标指针显示超链接的样式,否则则显示禁止的指针样式。

move :被拖拽元素将被移动到目标元素内,若属于 effectAllowed 范围内时,则鼠标指针显示移动的样式,否则则显示禁止的指针样式。

none :被拖拽元素不能在目标元素上作任何操作,一直显示禁止的指针样式。除了文本框外其他元素的默认值均为none

注意:   

  1. 仅能在 dragover 事件中设置该属性值,其他事件中设置均无效

  2. 当显示禁止的指针样式时,将无法触发目标元素的 drop 事件。
effectAllowed
作用:用于设置被拖拽元素可执行的操作。

取值范围:

copy ,限定dropEffect的属性值为copy,否则会鼠标指针为禁止样式

link ,限定dropEffect的属性值为link,否则会鼠标指针为禁止样式

move ,限定dropEffect的属性值为move,否则会鼠标指针为禁止样式

copyLink ,限定dropEffect的属性值为copy和link,否则会鼠标指针为禁止样式

copyMove ,限定dropEffect的属性值为copy和move,否则会鼠标指针为禁止样式

linkMove ,限定dropEffect的属性值为link和move,否则会鼠标指针为禁止样式

all ,允许dropEffect的属性值为任意值

none ,鼠标指针一直为禁止样式,不管dropEffect的属性值是什么

uninitialized ,没有限定dropEffect属性的值,效果和 all 一样。

注意:仅能在 dragstart 事件中设置该属性,其他事件中设置均无效。

下面做一些简单的测试

关于effectAllowed和dropEffect,这里将前者置为effectAllowed后者用下拉列表选择,以便于看到不同的鼠标样式。

<meta charset="UTF-8">
<title>testEvents</title>
<style type="text/css">
    .source{
        width: 50px;
        height: 50px;
        border: 1px solid red;
    }
    .process{
        width: 100px;
        height: 100px;
        border: 1px solid black;
        margin-top: 10px;
    }
    .dest{
        width: 100px;
        height: 100px;
        border: 1px solid green;
        margin-top: 10px;
    }
</style>
 
 
<select id="dpe">
    <option value="copy">copy</option>
    <option value="move">move</option>
    <option value="link">link</option>
    <option value="none">none</option>
</select>
<div class="source" id="source" draggable="true"></div>
<div class="process" id="process"></div>
<div class="dest" id="dest"></div>
<script type="text/javascript">
window.onload=function(){
    var source = document.getElementById("source");
    var process = document.getElementById("process");
    var dest = document.getElementById("dest");
    var dpe = document.getElementById("dpe");
    var dpev;
 
    dpe.onchange = function(){
        dpev = this.value;
    }
 
    var sourceEle;
 
    source.addEventListener("dragstart",function(e){
        console.log("source dragstart");
        console.log(e);
        sourceEle = e.target;
        var dt = e.dataTransfer;
        dt.effectedAllowed = "all";
    },false);
 
    dest.addEventListener("dragend",function(e){
        console.log("dest dragend");
        console.log(e);
        e.preventDefault();
    },false);
 
    dest.addEventListener("drop",function(e){
        console.log("dest drop");
        console.log(e);
        dest.appendChild(sourceEle);
        e.preventDefault();
        e.stopPropagation();
    },false);
 
    document.ondragover = function(e){
        e.dataTransfer.dropEffect = dpev;
        e.preventDefault();
    }
    document.ondrop = function(e){e.preventDefault();}
}
</script>

我在ubuntu chrome测试法现,都是一个手,但是置为none时拖不进去了,可能不同系统会有一些差别。

关于setData()和getData()方法
这两个是有关数据交换的方法,前者传两个参数,第一参数是一个mime类型字符串,第二个是数据;后者传一个参数,为mime类型。可用mime类型为text/plain,text/html,text/xml,text/uri-list.

测试用例,将菜单的菜拖到记录本上。

<meta charset="UTF-8">
<title>点菜</title>
<style type="text/css">
    .menu{
        width: 200px;
        height: 300px;
        border: 1px solid red;
        margin-right: 10px;
        float: left;
    }
    .record{
        width: 200px;
        height: 300px;
        border: 1px solid black;
        margin-right: 10px;
        float: left;
    }
</style>
 
 
<ul class="menu" id="menu">
    <li draggable="true">糖醋排骨</li>
    <li draggable="true">青椒肉丝</li>
    <li draggable="true">武昌鱼</li>
    <li draggable="true">手撕包材</li>
    <li draggable="true">千叶豆腐</li>   
</ul>
<ul class="record" id="record">
     
</ul>
<script type="text/javascript">
    window.onload = function(){
        var menu = document.getElementById("menu");
        var record = document.getElementById("record");
 
        menu.addEventListener("dragstart",function(e){
            var dt = e.dataTransfer;
            var tar = e.target;
            if(tar.tagName=="LI"){
                dt.setData("text/plain",tar.innerHTML);
            }
            dt.effectedAllowed = "all";
        },false);
 
        record.addEventListener("drop",function(e){
            var li  = document.createElement("li");
            li.appendChild(document.createTextNode(e.dataTransfer.getData("text/plain")));
            record.appendChild(li);
            e.stopPropagation();
        },false);
 
        record.addEventListener("dropend",function(e){
            e.preventDefault();
        },false);
 
        document.addEventListener("dragover",function(e){e.preventDefault()},false);
 
        document.addEventListener("drop",function(e){e.preventDefault()},false);
    }
</script>

关于setDragImage(Element img,long x,long y)
这个方法是设置拖放时的图标的,第一个参数表是图标元素,第二个表示相对与光标的水平偏移,第三个是垂直的。

还是前面的例子,在dragstart事件添加下面的代码,拖动时你会发现一只很大的手(不要被吓到);

var img = document.createElement("img");
                dt.setDragImage(img,10,10);

关于拖放数据传递

上面的例子已经谈到了拖放的数据传递方法,这里在总结一下。

  1、通过dataTransfer的setData()和getData()方法传递

  2、通过闭包的方法,请参考开篇的例子。

总结

HTML5的拖放api非常简洁实用,为我们省去了很多麻烦,如果没有它,我们可能需要通过mousedownmousemove等等事件才能实现上述功能。本文较为详细的介绍了相关api,希望对你有所帮助。关于拖放api的应用大家可以参看下面链接的文章,他做了一个拖放排序,这是一个比较常见的应用场景。

大~熊同学的粉丝数正在逼近三位数,感谢各位园友的支持,大~熊会继续努力的! 

参考:

原文地址:http://www.cnblogs.com/floor/p/6801921.html