原生Javascript+HTML5一步步实现拖拽排序

本文的内容就如题所述,之所以写这么个东西是有原因的,因为这两天重做公司的一个功能发现里面需要一个拖拽排序的功能.以前都是使用jquery各种插件去搞,因为这个项目不限制浏览器兼容问题就打算用HTML5来弄,走在时代的前沿不是,后来发现一个个属性那么写有点麻烦,就搜到一个HTML5的排序插件(纯粹抱着试试看的心里...不解释),没想到尼玛这插件竟然要jquery1.7以上支持,而且尼玛还是jquery~他妹的,不带你玩了,自己写~

Posted in Javascript onJune 12, 2021

    对HTML5有过了解的童鞋一定会知道它可以说非常使用的一个新特性,就是出现了元素拖放的接口,具体的API想详细了解的建议直接w3school去了解.

    (1) 通过draggable属性使你的元素可拖拽.

    这里我会对用到东西一点点的做出声明以及解释,铺垫的差不多了开始上干货,先构建一下基础的html和css,因为本文主旨不是样式所以没搞得太美观,就是OSC的绿以及中国红为主色,别介意:

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <title>HTML5-Drag-Demo by 顽Shi</title>
  <style>
    .column {
      height: 200px;
      width: 200px;
      float: left;
      border: 2px solid black;
      background-color: green;
      margin-right: 5px;
      text-align: center;
      cursor: move;
    }
    .column header {
      color: black;
      text-shadow: #000 0 1px;
      box-shadow: 5px;
      padding: 5px;
      background: red;
      border-bottom: 1px solid black;
    }
  </style>
</head>
<body>
  <div id="columns">
    <div draggable="true"><header>div1</header></div>
    <div draggable="true"><header>div2</header></div>
    <div draggable="true"><header>div3</header></div>
  </div>
</body>
</html>

    这就是html部分的代码,我们会逐渐完善它,效果图如下:

原生Javascript+HTML5一步步实现拖拽排序

    html和css代码很简单我就不解释了,有疑问的请留言的说...这里我们只是为div加上了一个draggable的属性,并且将属性值设为true.这就是HTML5新增的一个属性,将一个元素设置为可拖拽.其实如果大家细心会发现浏览器上看到的很多内容都是可拖拽的,比如链接或者图片,我举一个百度首页的例子截图给大家,可以看到我拖拽百度logo也是可以的,而且它的代码上也没有draggables属性.

原生Javascript+HTML5一步步实现拖拽排序

    这就是纯粹的浏览器支持,默认情况下大多数浏览器对于带有href属性的内容,比如超链接和图片等都是支持拖拽的,但是这个拖拽和我们今天要讲的DOM拖拽是不一样的.回到我们的demo,此时div1,div2,div3都可以进行拖拽了,类似与复制了一个自己的感觉,如果松开鼠标还会有一个滑回原来位置的默认动作.截图如下:

原生Javascript+HTML5一步步实现拖拽排序

    拖拽动作在大多数浏览器下会创建一个自身的副本留在原来位置,这是一个很重要的东西,我们接下来会用到.

    (2) 对拖拽的动作进行监听

    现在元素可以移动了,但是距离我们的目标还有一定的差距,我们想做的是什么?

    我们希望的效果是元素被拖起来之后,可以在达到指定区域后,通过松开鼠标的动作将它固定在一个我们指定的位置上.所以我们要对这个过程中涉及到的动作加以监控.(不是所有的区域都能指定为元素被固定的区域,比如说图片就能接收其他元素).

    在拖动开始后我们希望被拖动的元素能变得透明一些,以区分出拖动和未拖动的不同,监听ondrugstart可以轻松实现,代码如下:

<script>
  var columns = document.querySelectorAll('#columns .column');
  
  [].forEach.call(columns,function(column){
    column.addEventListener("dragstart",domdrugstart,false);
  });
  
  function domdrugstart(e) {
    e.target.style.opacity = '0.5';
  }
</script>

    此时拖动一个元素,它的透明度会变为50%.不过拖动结束后这一效果还保留着,所以要在拖动结束重置会100%.这个留到最后实现即可.

    接下来我们实现的一个效果,是要在拖拽时将其可能放置的位置标识出来,显示出哪里是可能放置的位置,尤其在鼠标悬停或者划过的时候.这一过程需要ondragenter,ondragover以及ondragleave这三个事件的监听.代码如下:

    .column.over {
      border: 3px dashed #000;
    }
    //将这个样式添加到style中
    
    <script>
      var columns = document.querySelectorAll('#columns .column');
      
      [].forEach.call(columns,function(column){
        column.addEventListener("dragstart",domdrugstart,false);
        column.addEventListener('dragenter', domdrugenter, false);
        column.addEventListener('dragover', domdrugover, false);
        column.addEventListener('dragleave', domdrugleave, false);
      });
      
      function domdrugstart(e) {
        e.target.style.opacity = '0.5';
      }
      function domdrugenter(e) {
        e.target.classList.add('over');
      }
      function domdrugover(e) {
        if (e.preventDefault) {
          e.preventDefault(); 
        }
    
        e.dataTransfer.dropEffect = 'move';
    
        return false;
      }
      function domdrugleave(e) {
        e.target.classList.remove('over'); 
      }      
    </script>

    解释一下代码,dragenter事件的动作是拖动后鼠标进入另一个可接受区域,此时给添加了监听的对象加上一个虚线的边框,之所以使用dragenter而不是dragover是因为前者只在进入区域时触发一次,后者会反复触发.dragover则是为了阻止一些类似超链接拖动跳转的默认动作,dragleave则是去掉当拖拽鼠标划出区域时,去掉虚线.

    (3)  添加释放动作

    到此为止元素已经可以进行拖拽,并且并且在拖拽的过程有虚线的提示.但是现在还缺少一个完成的动作,也就是拖拽结束的动作,这个动作要有两个作用,一是和over类似去阻止浏览器的默认动作,二是根据我们的需求进行DOM操作.

    这里用到drop以及dragend:

<script>
  var columns = document.querySelectorAll('#columns .column');
  
  [].forEach.call(columns,function(column){
    column.addEventListener("dragstart",domdrugstart,false);
    column.addEventListener('dragenter', domdrugenter, false);
    column.addEventListener('dragover', domdrugover, false);
    column.addEventListener('dragleave', domdrugleave, false);
    column.addEventListener('drop', domdrop, false);
    column.addEventListener('dragend', domdrapend, false);    
  });
  
  function domdrugstart(e) {
    e.target.style.opacity = '0.5';
  }
  function domdrugenter(e) {
    e.target.classList.add('over');
  }
  function domdrugover(e) {
    if (e.preventDefault) {
      e.preventDefault(); 
    }

    e.dataTransfer.dropEffect = 'move';

    return false;
  }
  function domdrugleave(e) {
    e.target.classList.remove('over'); 
  }   
  function domdrop(e) {
    if (e.stopPropagation) {
      e.stopPropagation();
    }
    return false;
  }
  function domdrapend(e) {
    [].forEach.call(columns, function (column) {
      column.classList.remove('over');
       column.style.opacity = '1';
    });
  }     
</script>

    至此我们会发现,还没有完成元素的交换这一动作,只是让所有的元素看起来可拖拽了而已,因为还缺少最后的DataTransfer对象.这是一个很神奇的东西,它使拖动的过程中可以发送数据,可以在dragstart中设置并且在drop或者dragend时读取.

    这里我们先看添加的代码:

  var dragEl = null;

  function domdrugstart(e) {
    e.target.style.opacity = '0.5';
    
    dragEl = this;
    
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html",this.innerHTML);
  }
  
  function domdrop(e) {
    if (e.stopPropagation) {
      e.stopPropagation();
    }
    
    if (dragEl != this) {
      dragEl.innerHTML = this.innerHTML;
      this.innerHTML = e.dataTransfer.getData('text/html');
    }    
    
    return false;
  }

    效果如图:

原生Javascript+HTML5一步步实现拖拽排序

    最后这段代码可能大家会有疑惑,解释一下.首先定义了一个全局变量dragEl它的作用就是用来存储被拖拽元素的html,然后在释放拖拽时和释放区域的元素进行交换.e.dataTransfer.effectAllowed这个是用来限制元素的,它限制了元素的拖动类型为move.紧接着通过代码将被拖动元素的html进行setData.然后这里set的值可以通过get进行获取.

    基本上到这就完成了整个功能,稍微进行一下封装就可以形成插件,这个有兴趣的可以自己搞~最后附源码:

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <title>HTML5-Drag-Demo by 顽Shi</title>
  <style>
    .column {
      height: 200px;
      width: 200px;
      float: left;
      border: 1px solid black;
      background-color: green;
      margin-right: 5px;
      text-align: center;
      cursor: move;
    }
    .column header {
      color: black;
      text-shadow: #000 0 1px;
      box-shadow: 5px;
      padding: 5px;
      background: red;
      border-bottom: 1px solid black;
    }
    .column.over {
      border: 3px dashed #000;
    }
  </style>
</head>
<body>
  <div id="columns">
    <div class="column" draggable="true"><header>div1</header></div>
    <div class="column" draggable="true"><header>div2</header></div>
    <div class="column" draggable="true"><header>div3</header></div>
  </div>
</body>
<script>
  var columns = document.querySelectorAll('#columns .column');
  
  var dragEl = null;
  
  [].forEach.call(columns,function(column){
    column.addEventListener("dragstart",domdrugstart,false);
    column.addEventListener('dragenter', domdrugenter, false);
    column.addEventListener('dragover', domdrugover, false);
    column.addEventListener('dragleave', domdrugleave, false);
    column.addEventListener('drop', domdrop, false);
    column.addEventListener('dragend', domdrapend, false);    
  });
  
  function domdrugstart(e) {
    e.target.style.opacity = '0.5';
    
    dragEl = this;
    
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html",this.innerHTML);
  }
  function domdrugenter(e) {
    e.target.classList.add('over');
  }
  function domdrugover(e) {
    if (e.preventDefault) {
      e.preventDefault(); 
    }

    e.dataTransfer.dropEffect = 'move';

    return false;
  }
  function domdrugleave(e) {
    e.target.classList.remove('over'); 
  }   
  function domdrop(e) {
    if (e.stopPropagation) {
      e.stopPropagation();
    }
    
    if (dragEl != this) {
      dragEl.innerHTML = this.innerHTML;
      this.innerHTML = e.dataTransfer.getData('text/html');
    }    
    
    return false;
  }
  function domdrapend(e) {
    [].forEach.call(columns, function (column) {
      column.classList.remove('over');
       column.style.opacity = '1';
    });
  }     
</script>
</html>
 
Javascript 相关文章推荐
js+html+css实现鼠标移动div实例
Jan 30 Javascript
详谈jQuery中的this和$(this)
Nov 13 Javascript
基于JavaScript操作DOM常用的API小结
Dec 01 Javascript
返回函数的JavaScript函数
Jun 14 Javascript
实例详解jQuery的无new构建
Aug 02 Javascript
深入浅析JS是按值传递还是按引用传递(推荐)
Sep 18 Javascript
老生常谈原生JS执行环境与作用域
Nov 22 Javascript
解析如何利用iframe标签以及js制作时钟
Dec 08 Javascript
js通过指定下标或指定元素进行删除数组的实例
Jan 12 Javascript
AngularJS日程表案例详解
Aug 15 Javascript
TypeScript之调用栈的实现
Dec 31 Javascript
Vue elementUI表单嵌套表格并对每行进行校验详解
Feb 18 Vue.js
JS代码编译器Monaco使用方法
React中的Context应用场景分析
Jun 11 #Javascript
详解JVM系列之内存模型
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
一文搞懂redux在react中的初步用法
Jun 09 #Javascript
深入详解JS函数的柯里化
Jun 09 #Javascript
javascript canvas实现雨滴效果
You might like
无线电广播的开始
2002/01/30 无线电
PHP利用func_get_args和func_num_args函数实现函数重载实例
2014/11/12 PHP
PHP PDO和消息队列的个人理解与应用实例分析
2019/11/25 PHP
javascript小数四舍五入多种方法实现
2012/12/23 Javascript
使用js判断数组中是否包含某一元素(类似于php中的in_array())
2013/12/12 Javascript
js实现简单的购物车有图有代码
2014/05/26 Javascript
JQuery遍历元素的后代和同胞实现方法
2016/09/18 Javascript
JavaScript实现的浏览器下载文件的方法
2017/08/09 Javascript
react开发教程之React 组件之间的通信方式
2017/08/12 Javascript
HTML5+JS+JQuery+ECharts实现异步加载问题
2017/12/16 jQuery
基于JS实现html中placeholder属性提示文字效果示例
2018/04/19 Javascript
js 图片转base64的方式(两种)
2018/04/24 Javascript
JavaScript实现一个简易的计算器实例代码
2018/05/10 Javascript
在react中使用vue的状态管理的方法示例
2020/05/02 Javascript
[01:30:55]VG vs Mineski Supermajor 败者组 BO3 第三场 6.6
2018/06/07 DOTA
[52:39]完美世界DOTA2联赛PWL S3 CPG vs Forest 第一场 12.16
2020/12/17 DOTA
[59:48]DOTA2-DPC中国联赛 正赛 VG vs Magma BO3 第一场 1月26日
2021/03/11 DOTA
Python中的闭包详细介绍和实例
2014/11/21 Python
python下如何查询CS反恐精英的服务器信息
2017/01/17 Python
利用python爬取斗鱼app中照片方法实例
2017/12/03 Python
python 与服务器的共享文件夹交互方法
2018/12/27 Python
Python3获取拉勾网招聘信息的方法实例
2019/04/03 Python
pyqt5 从本地选择图片 并显示在label上的实例
2019/06/13 Python
在python shell中运行python文件的实现
2019/12/21 Python
在CentOS7下安装Python3教程解析
2020/07/09 Python
python创建文本文件的简单方法
2020/08/30 Python
CSS3等相关属性制作分页导航实现代码
2012/12/24 HTML / CSS
域名注册、建站工具、网页主机、SSL证书:Dynadot
2017/01/06 全球购物
德国网上药房:Apotal
2017/04/04 全球购物
通往英国高街的商店橱窗:Down Your High Street
2020/07/19 全球购物
成人大专生实习期的自我评价
2013/10/02 职场文书
车辆安全检查制度
2014/01/12 职场文书
入职担保书怎么写
2014/05/12 职场文书
旅游活动总结
2014/08/27 职场文书
2015年暑假工作总结
2015/07/13 职场文书
母亲节感言
2015/08/03 职场文书