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

  前言css

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

  正文jquery

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

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

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

<!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部分的代码,咱们会逐渐完善它,效果图以下:
spa

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

    这就是纯粹的浏览器支持,默认状况下大多数浏览器对于带有href属性的内容,好比超连接和图片等都是支持拖拽的,可是这个拖拽和咱们今天要讲的DOM拖拽是不同的.回到咱们的demo,此时div1,div2,div3均可以进行拖拽了,相似与复制了一个本身的感受,若是松开鼠标还会有一个滑回原来位置的默认动做.截图以下:
code

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

    (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;
  }

    效果如图:

    最后这段代码可能你们会有疑惑,解释一下.首先定义了一个全局变量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>
相关文章
相关标签/搜索