原生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>
相關文章
相關標籤/搜索