前言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>