原生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 相关文章推荐
使Ext的Template可以解析二层的json数据的方法
Dec 22 Javascript
ExtJs Excel导出并下载IIS服务器端遇到的问题
Sep 16 Javascript
js读取被点击次数的简单实例(从数据库中读取)
Mar 07 Javascript
浅谈Javascript如何实现匀速运动
Dec 19 Javascript
JavaScript数据库TaffyDB用法实例分析
Jul 27 Javascript
基于BootStrap Metronic开发框架经验小结【四】Bootstrap图标的提取和利用
May 12 Javascript
Vue.js做select下拉列表的实例(ul-li标签仿select标签)
Mar 02 Javascript
在Vue-cli里应用Vuex的state和mutations方法
Sep 16 Javascript
JS实现数组去重及数组内对象去重功能示例
Feb 02 Javascript
浅析Angular 实现一个repeat指令的方法
Jul 21 Javascript
vue使用微信JS-SDK实现分享功能
Aug 23 Javascript
vue vantUI tab切换时 list组件不触发load事件的问题及解决方法
Feb 14 Javascript
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
PHP临时文件的安全性分析
2014/07/04 PHP
PHP多维数组遍历方法(2种实现方法)
2015/12/10 PHP
PHP模糊查询的实现方法(推荐)
2016/09/06 PHP
PHP环境搭建(php+Apache+mysql)
2016/11/14 PHP
PHP函数用法详解【初始化、嵌套、内置函数等】
2020/06/02 PHP
jQuery 1.0.4 - New Wave Javascript(js源文件)
2007/01/15 Javascript
javascript温习的一些笔记 基础常用知识小结
2011/06/22 Javascript
js兼容火狐获取图片宽和高的方法
2015/05/21 Javascript
BootStrap 轮播插件(carousel)支持左右手势滑动的方法(三种)
2016/07/07 Javascript
jQuery simpleModal插件的使用介绍
2016/08/30 Javascript
正则表达式替换html元素属性的方法
2016/11/26 Javascript
JavaScript中Promise的使用详解
2017/02/26 Javascript
javascript图片预览和上传(兼容IE)
2017/03/15 Javascript
通过命令行生成vue项目框架的方法
2017/07/12 Javascript
uni app仿微信顶部导航条功能
2019/09/17 Javascript
layui 上传图片 返回图片地址的方法
2019/09/26 Javascript
小程序api实现promise封装过程解析
2019/11/21 Javascript
vue.js页面加载执行created,mounted的先后顺序说明
2020/11/07 Javascript
在Mac OS系统上安装Python的Pillow库的教程
2015/11/20 Python
matplotlib中legend位置调整解析
2017/12/19 Python
使用python PIL库实现简单验证码的去噪方法步骤
2019/05/10 Python
python线程池如何使用
2020/05/28 Python
Python 连接 MySQL 的几种方法
2020/09/09 Python
计算机专业学生求职信分享
2013/12/15 职场文书
工会主席岗位责任制
2014/02/11 职场文书
优秀食品类广告词
2014/03/19 职场文书
探亲假请假条
2014/04/11 职场文书
企业精神口号
2014/06/11 职场文书
没有孩子的离婚协议书怎么写
2014/09/17 职场文书
学校领导班子成员查摆问题及整改措施
2014/10/28 职场文书
教师自查自纠工作情况报告
2014/10/29 职场文书
《改造我们的学习》心得体会
2014/11/07 职场文书
2014年禁毒工作总结
2014/11/24 职场文书
中学推普周活动总结
2015/05/07 职场文书
html中显示特殊符号(附带特殊字符对应表)
2021/06/21 HTML / CSS
Python使用openpyxl模块处理Excel文件
2022/06/05 Python