你們好,我是練習js時長接近兩年半的我的練習生--李大雷css
算了,直接 雞,你太美~html
不少時候,咱們須要讓用戶來自定義本身想要的菜單順序,或者一些按鈕的排序,那麼這個時候,怎麼給用戶自定義順序呢?
拖拽無疑是最簡單易懂的,由於玩過手機的都知道怎麼拖動桌面的app來改變位置。app
那麼要怎麼作呢?最簡單的方式確定是用H5的拖放啦~dom
首先咱們先來看看,這兩個單詞,drag--拖,drop--放,從這裏就很容易看出來,這裏的操做邏輯了。
咱們來看看有哪些事件能夠給咱們使用。學習
咱們來舉例子說明一下
<div class="A" draggable="true" ondragstart="fn()" ondrag="fn()" ondragend="fn()"></div> <div class="B" ondragenter="fn()" ondragover="fn()" ondragleave="fn()" ondrop="fn()"></div>
假設有div A和div B,當我按住A,開始拖動(A dragstart觸發一次)(drag在你移動的時候不斷觸發),而後你通過了B(B觸發了dragenter事件),而後你在B裏瘋狂摩擦(那就瘋狂觸發B的dragover,這句話怎麼越讀越不對勁?),而後你從B中出來(那就觸發了B的dragleave),而後又進入B中(而且放開鼠標,那麼就會觸發B的drop和A的dragend);flex
對於A來講,它的事件就前面3個,對於B來講,它的事件就是後面4個;
A是攻,那麼B就是受了。固然你也能夠自攻自受,就像孟德爾的自交豌豆同樣
咱們下面作的拖拽也是自攻自受的狀況,由於你可能拖動A和B交換,也可能拖動B來和A交換位置。this
一些須要注意的點:
這個標題的cao是第一聲。
通過咱們上面的一頓基礎知識學習之後呢,咱們就很容易想清楚這個實現邏輯。spa
把A設置爲能夠拖動,當A拖動到B的時候,咱們就互換A和B兩個dom節點。
至於怎麼互換呢?咱們能夠直接調換兩個節點的內容,或者咱們調換兩個dom節點的位置兩種方法,這裏我用的是第一種方法,第二種留給你們去嘗試啦~3d
1. 咱們先寫一個大概的樣式code
2. html結構以下
<div class="card" draggable="true" ondrag="handleDrag(event,this)" ondragstart="handleDragStart(event,this)" ondragover="handleDragOver(event,this)" ondragend="handleDragEnd(event,this)" ondrop="handleDrop(event,this)" ondragenter="handleDragEnter(event,this)"> <span class="card-name"> ${title} </span> <div class="card-img"> <img src="${src}" draggable="false" alt=""> </div> </div>
3. 開始寫邏輯,請仔細查看註釋
//先定義兩個變量來保存源元素,以及目標元素,還有記錄一下上次交換的dom //爲何要這一步呢?日後面看 let fromDom = null, toDom = null, lastDom = null; //開始拖拽 function handleDragStart(e, dom) { //開始拖拽的時候,把來源保存下來 fromDom = dom; } //拖拽中 function handleDrag(){ console.log('若是你有業務邏輯的話,你能夠寫,可是我沒有,抱歉') } //拖到了另外一個div中,這個時候的dom就是另外一個元素了哦 function handleDragEnter(e, dom) { //保存目標元素 toDom = dom; if(fromDom == lastDom){ //第一次調換 //爲何要分爲幾回調換位置呢? //想一下,若是我剛A和B調換了位置,那麼就是B和A了可是此時個人鼠標尚未鬆開! //那麼我又移動到C,那麼互換的位置就是B和C了,可是其實我一開始拖拽的是A,我只想換AC只是不當心路過了B! //所以咱們這裏就要使用一個lastDom來記錄上次路過交換的DOM,同時也要區分第幾回調換。 swapDom(lastDom, toDom); //記錄新的‘上一個dom’ lastDom = toDom; }else{ //這個防止enter屢次觸發 if(lastDom == toDom){return;} //第N+1次調換,要先把上一個div的東西還原回去,再跟第三個div互換 swapDom(fromDom,lastDom); swapDom(fromDom,toDom); //記錄新的‘上一個dom’ lastDom = toDom; } } //在B中移動 function handleDragOver(e, dom) { //默認沒法把元素放置到其餘元素當中,若是這個不寫,沒法交換div的innerHTML值,因此須要阻止默認事件,這一步很重要!! e.preventDefault(); } //放手 function handleDragEnd(e,dom){ //拖拽時鬆開鼠標就會會觸發dragend事件,這個dom是拖拽的節點。 //重置toDom,下次拖拽就是新拖拽了,fromDom和lastDom會在dragStart的時候重置 toDom = null; } //有上面那個,其實這個能夠省略了。 function handleDrop(e, dom) { //只有在可放置的元素上面鬆開鼠標纔會觸發drop事件,因此這個dom是被放置的dom節點。 //重置toDom,下次拖拽就是新拖拽了,fromDom和lastDom會在dragStart的時候重置 toDom = null; } //交換dom內容 function swapDom(from, to) { let temp = a.innerHTML; a.innerHTML = b.innerHTML; b.innerHTML = temp; }
其實咱們用不上那麼多事件回調,主要的是 開始拖拽保存來源,進入目標時,保存目標,而且通過判斷後交換,交換完之後,咱們就把目標重置,完事~
邏輯比較簡單,不過寫動態css比較麻煩(由於咱們須要一些css的效果來分辨哪一個是被你拖動的,那個又互換了位置之類的,有比較好的用戶體驗),剛開始寫常常傻傻分不清是來源dom仍是目標dom~
此部分適合新手玩家,由於本身只是隨意寫寫,並無寫得很規範,但願你們不要學習!
<!DOCTYPE html> <html> <head> <title></title> <style> body { margin: 0; } .box { display: flex; justify-content: flex-start; flex-wrap: wrap; } .card { flex: 1; min-width: 26%; max-width: calc(33.3% - 40px); height: 200px; margin: 30px 10px; position: relative; padding: 10px; box-shadow: 0 2px 5px 0 #999; border-radius: 5px; border: 2px dashed transparent; } .card-name { position: absolute; top: 10px; left: 10px; line-height: 20px; height: 20px; } .card-img { position: relative; padding-top: 20px; box-sizing: border-box; width: 100%; height: 100%; overflow: hidden; } .card-img img { width: 100%; height: 100%; } .dragging-over * { pointer-events: none; } </style> </head> <body> <div class="box"> </div> </body> <script> const htmlArr = [ { title: '示例1-風景', src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2756575517,833879878&fm=200&gp=0.jpg' }, { title: '示例2-風景', src: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=328517395,2303970886&fm=26&gp=0.jpg' }, { title: '示例3-風景', src: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554369684535&di=1c1dbfbd4545ad0a05e12cbbbfe3eeef&imgtype=0&src=http%3A%2F%2Fpic41.nipic.com%2F20140601%2F18681759_143805185000_2.jpg' }, { title: '示例4-風景', src: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554369684534&di=d6e34af6fce6564f9df6c4eecc27d2ce&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimgad%2Fpic%2Fitem%2F4d086e061d950a7b9138ff1000d162d9f3d3c9d1.jpg' }, ] let fromDom = null, toDom = null, lastDom = null; function handleDragStart(e, dom) { lastDom = fromDom = dom; dom.style.border = "2px dashed #999"; dom.style.opacity = 0.4; } function handleDrop(e, dom) { //只有在可放置的元素上面鬆開鼠標纔會觸發drop事件 console.log('drop'); dom.style.opacity = ""; fromDom = null; toDom = null; } function handleDragEnd(e,dom){ //拖拽時鬆開鼠標就會會觸發dragend事件 console.log('end'); dom.style.border = "2px dashed transparent"; dom.style.opacity = ""; toDom = null; } function handleDragEnter(e, dom) { toDom = dom; if(fromDom == lastDom){ //第一次調換 swapDom(lastDom, toDom); lastDom = toDom; }else{ //第N+1次調換,要先把上一個div的東西還原回去,再跟第三個div互換 //這個防止enter屢次觸發 if(lastDom == toDom){return;} swapDom(fromDom,lastDom); swapDom(fromDom,toDom); lastDom = toDom; } } function handleDragOver(e, dom) { //默認沒法把元素放置到其餘元素當中,因此須要prevent e.preventDefault(); e.dataTransfer.effectAllowed = "move"; } function swapDom(a, b) { //a和b的innerHTML互換 let temp = a.innerHTML; a.innerHTML = b.innerHTML; b.innerHTML = temp; } //生成dom結構 function createDom(arr) { let body = document.getElementsByClassName('box')[0]; let html = []; for (let i = 0, len = arr.length; i < len; i++) { html.push(template(arr[i].title, arr[i].src)); } body.innerHTML = html.join(''); } //html模板,根據該模板動態生成dom節點 function template(title, src) { let tpl = `<div class="card" draggable="true" ondragstart="handleDragStart(event,this)" ondragover="handleDragOver(event,this)" ondragend="handleDragEnd(event,this)" ondrop="handleDrop(event,this)" ondragenter="handleDragEnter(event,this)"> <span class="card-name"> ${title} </span> <div class="card-img"> <img src="${src}" draggable="false" alt=""> </div> </div>` return tpl; } window.onload = function() { createDom(htmlArr); } </script> </html>
其實在沒有這個drag以前,是用鼠標事件來實現的,這裏就簡單講講思路好了,懶得寫了~
謝謝你們,但願你們寫代碼不要像cxk。