前端工程师小A学习JS的旅程
前端工程师小A学习JS的旅程
在网页里,拖拽效果是很常见的形式,实现起来也比较简单,但是这么简单的代码,却能体现出很多前端开发工程湿的JS能力,我们来看看小A工程师的实现路程:
HTML代码:
<!DOCTYPE HTML> <html> <head> <title>拖拽效果</title> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"> <style type="text/css"> #drag_box{width: 150px;height: 150px;background-color: #ff0000;position: absolute;margin: 0;top: 80px;left: 0;} #diy,#dix{width: 70px;display: inline-block;height: 30px;} </style> </head> <body> <span id="dix">X:0px</span> <input type="button" value="锁定X轴" id="closeX" data-open="true"> <br/> <span id="diy">Y:80px</span> <input type="button" value="锁定Y轴" id="closeY" data-open="true"> <div id="drag_box"></div> </body> </html>
JS代码:
小A的拖拽JS代码第一版
<script type="text/javascript"> var closeX=document.getElementById("closeX"),closeY=document.getElementById("closeY"); closeX.onclick=function(){ if(this.getAttribute("data-open")=="true"){ this.setAttribute("data-open","false"); this.value="解开X轴"; }else{ this.setAttribute("data-open","true"); this.value="锁定X轴"; } } closeY.onclick=function(){ if(this.getAttribute("data-open")=="true"){ this.setAttribute("data-open","false"); this.value="解开Y轴"; }else{ this.setAttribute("data-open","true"); this.value="锁定Y轴"; } } var box=document.getElementById('drag_box'); var x=y=0; var dix=document.getElementById("dix"),diy=document.getElementById("diy"); box.onmousedown=function(event){ var event=event||window.event; x=event.clientX-this.offsetLeft; y=event.clientY-this.offsetTop; this.style.cursor="move"; var that = this; document.onmousemove=function(event){ var event=event||window.event; var le=event.clientX-x,to=event.clientY-y; var maxL = document.documentElement.clientWidth - box.offsetWidth; var maxT = document.documentElement.clientHeight - box.offsetHeight; if(le<0){ le=0 }else if(le>maxL){ le=maxL; } if(to<80){ to=80 }else if(to>maxT){ to=maxT; } if(closeX.getAttribute("data-open")=="true"){ that.style.left=le+"px"; dix.innerHTML="X:"+le+"px"; } if(closeY.getAttribute("data-open")=="true"){ that.style.top=to+"px"; diy.innerHTML="Y:"+to+"px"; } if(that.releaseCapture){ that.releaseCapture(); } return false; } this.onmouseup=function(){ document.onmousemove=null; document.onmouseup = null; this.style.cursor="default"; if(this.releaseCapture){ this.releaseCapture(); } return false; } if(this.releaseCapture){ this.releaseCapture(); } return false; } </script>
这是小A初学JS一个月写出来的JS代码,完美实现了拖拽的功能,并且也能也能注意在适当的时候阻止事件的默认行为,还能用上if(this.releaseCapture){this.releaseCapture();}这样的语句来防止鼠标离开拖拽BOX。对于小A来说,学习一个月能写出这样的代码来已经实属不易,我们也不得不承认小A是一个学习能力很强的前端工程师。
又学习了一个月小A学到了js的封装,于是重新看了看上面的代码,觉得处理锁定X轴和锁定Y轴的代码有些重复,而且拖拽的对象可能会发生变化,于是小A改造了一下上面的代码,创建了两个方法函数drag和closexy把主体功能封转在两个函数里面:
小A的拖拽JS代码第二版
<script type="text/javascript"> function drag (box) { var x=y=0; var dix=document.getElementById("dix"),diy=document.getElementById("diy"); box.onmousedown=function(event){ var event=event||window.event; x=event.clientX-this.offsetLeft; y=event.clientY-this.offsetTop; this.style.cursor="move"; var that = this; document.onmousemove=function(event){ var event=event||window.event; var le=event.clientX-x,to=event.clientY-y; var maxL = document.documentElement.clientWidth - box.offsetWidth; var maxT = document.documentElement.clientHeight - box.offsetHeight; if(le<0){ le=0 }else if(le>maxL){ le=maxL; } if(to<80){ to=80 }else if(to>maxT){ to=maxT; } if(closeX.getAttribute("data-open")=="true"){ that.style.left=le+"px"; dix.innerHTML="X:"+le+"px"; } if(closeY.getAttribute("data-open")=="true"){ that.style.top=to+"px"; diy.innerHTML="Y:"+to+"px"; } if(that.releaseCapture){ that.releaseCapture(); } return false; } this.onmouseup=function(){ document.onmousemove=null; document.onmouseup = null; this.style.cursor="default"; if(this.releaseCapture){ this.releaseCapture(); } return false; } if(this.releaseCapture){ this.releaseCapture(); } return false; } } function closexy(xy,obj){ if(obj.getAttribute("data-open")=="true"){ obj.setAttribute("data-open","false"); obj.value="解开"+xy+"轴"; }else{ obj.setAttribute("data-open","true"); obj.value="锁定"+xy+"轴"; } } var box=document.getElementById('drag_box'); drag(box); var closeX=document.getElementById("closeX"),closeY=document.getElementById("closeY"); closeX.onclick=function(){ closexy("X",this); } closeY.onclick=function(){ closexy("Y",this); } </script>
这样一看,代码量减少了许多,而且显得也比较调理,更重要的是封转后的方法在随意的对象上都可以调用,简单、方便!此时的小A学习js的激情高涨,于是就更加勤奋的学习!
又不知过了多久。小A的js水平有了大幅提高,这个时候小A回头看之前写过的拖拽代码,觉得有很多不妥的地方,比如:没有注意命名空间,没有成模块化、如果拖拽的外围边界不是body了怎么变、如果拖拽的范围变化了呢,拖拽的锁定可不可以做成开放性的呢。基于这些问题,于是小A觉得完全可以把拖拽功能做成一个拖拽库,开放接口,以应付千变万化的拖拽效果和需求,所以小A又写了第三版的拖拽JS代码:
小A的拖拽JS代码第三版
<script type="text/javascript"> /* * * * dragBox:拖动对象,可以是对象的ID; * * options:参数; * * maxContainer:设置拖动外围box,默认为body; * * minL:离外围边界左边最小距离,默认为0; * * maxL:离外边界左边最大距离,默认body的宽度-拖动对象宽度; * * minT:离外围边界上边最小距离,默认为0; * * maxT:离外围边界最大高度,默认为body高度-拖动对象高度; * * lockx:是否锁定X轴,默认不锁定; * * locky:是否锁定Y轴,默认不锁定; * * onStart:开始拖动回调函数; * * onMove:拖动过程中回调函数; * * onStop:拖动结束回调函数; */ function Drag(){ this.init.apply(this,arguments); } Drag.prototype={ init:function(dragBox,options){ this.dragBox=this.$(dragBox); this.setOptions(options); this._moveDrag=this.bind(this,this.moveDrag); this._stopDrag=this.bind(this,this.stopDrag); this.maxContainer = this.options.maxContainer||document.documentElement || document.body; this.minL=this.options.minL||0; this.maxL=this.options.maxL||Math.max(this.maxContainer.clientWidth,this.maxContainer.scrollWidth)-this.dragBox.offsetWidth; this.minT=this.options.minT||0; this.maxT=this.options.maxT||Math.max(this.maxContainer.clientHeight,this.maxContainer.scrollHeight)-this.dragBox.offsetHeight; this.lockx = this.options.lockx||false; this.locky = this.options.locky||false; this.onStart=this.options.onStart||null; this.onMove=this.options.onMove||null; this.onStop=this.options.onStop||null; this.addHandle(this.dragBox, "mousedown", this.bind(this, this.startDrag)); this.haslayout(); }, haslayout:function(){ this.dragBox.style.left=this.minL+"px"; this.dragBox.style.top=this.minT+"px"; }, startDrag:function(event){ var event=event||window.event; this._x=event.clientX-this.dragBox.offsetLeft; this._y=event.clientY-this.dragBox.offsetTop; this.addHandle(document,"mousemove",this._moveDrag); this.addHandle(document,"mouseup",this._stopDrag); this.preventDefault(event); this.dragBox.setCapture && this.dragBox.setCapture(); if(typeof onStart==="function"){ this.onStart(); } }, moveDrag:function(event){ var event=event||window.event; this.dragBox.style.cursor = "move"; var le=event.clientX-this._x,to=event.clientY-this._y; if(le<this.minL){ le=this.minL }else if(le>this.maxL){ le=this.maxL; } if(to<this.minT){ to=this.minT; }else if(to>this.maxT){ to=this.maxT; } if(!this.lockx){ this.dragBox.style.left=le+"px"; } if(!this.locky){ this.dragBox.style.top=to+"px"; } this.preventDefault(event); this.dragBox.setCapture && this.dragBox.setCapture(); if(typeof onMove==="function"){ this.onMove(); } }, stopDrag:function(){ this.removeHandle(document,"mousemove",this._moveDrag); this.removeHandle(document,"mouseup",this._stopDrag); this.dragBox.style.cursor = "default"; this.dragBox.setCapture && this.dragBox.setCapture(); if(typeof onStop==="function"){ this.onStop(); } }, $:function(id){ return typeof id=="string" ? document.getElementById(id):id; }, addHandle:function(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false); }else if(element.attachEvent){ element.attachEvent("on"+type,handler); }else{ element["on"+type]=handler; } }, removeHandle:function(element,type,handler){ if(element.removeEventListener){ element.removeEventListener(type,handler,false); }else if(element.detachEvent){ element.detachEvent("on"+type,handler); }else{ element["on"+type]=null; } }, preventDefault:function(event){ if(event.preventDefault){ event.preventDefault(); }else{ event.returnValue=false; } }, setOptions : function(options){ this.options ={}; for (var p in options) this.options[p] = options[p]; }, bind : function (object, fnHandler){ return function (){ return fnHandler.apply(object, arguments) } } }; //应用 var box=document.getElementById("drag_box"); var Mydrag=new Drag(box,{ minT:10, maxT:100, lockx:true }); </script>
这样,一个功能齐全的拖拽库就这样漂亮的诞生了!拖拽库开放接口很齐全,代码页很规范。从此小A就不用害怕任何拖拽功能需求了!
从小A学习js的旅程,我们可以看出,学习JS是一个循序渐进的过程,看似简单的功能,从最开始的实现功能,到不断改进,一直到最后写成一个插件,可以有很多版本,而这些不断改进的过程正是学习JS最好的方法!