前端工程师小A学习JS的旅程
发布在javascript2014年2月14日view:2851
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

在网页里,拖拽效果是很常见的形式,实现起来也比较简单,但是这么简单的代码,却能体现出很多前端开发工程湿的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最好的方法!
评论
发表评论
7年前

好文

7年前

好文

WRITTEN BY
Breeze Feng
做一名优秀的web攻城师
TA的新浪微博
PUBLISHED IN
javascript

javascript系列

我的收藏