你们好,我是练习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。