原生js实现拖拽考虑浏览器窗口边界 详细版代码+注释

关于拖拽元素,在网上有很多例子,但都比较简单,且大多数没有完全考虑浏览器窗口边界。
参考多方资料,自己写了个较完善的封装拖拽的方法。
先贴出代码,复制后运行看下效果,
所有理解在注释中较详细说明。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
        #message{
            height: 50px;
            width: 200px;
            font-size: 30px;
            line-height: 50px;
            margin:0 auto;
        }
        .draggable{
            top:100px;
            left: 100px;
            height: 100px;
            width: 100px;
            background-color: red;
            position: absolute;
        }
    </style>
</head>
<body>
    <div id="message" draggable="false"></div><div class="draggable"></div>
    <script type="text/javascript">
         //标准事件流
        var EventUtil = {
            addHandler : 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;
                }
            },
            removeHandler: 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;
                }
            },
            getTarget: function(event){
                return event.target || event.srcElement;
            },
            //如果getEvent看不懂,在我下一篇博客参看
            getEvent: function(event){
                var e = event || window.event;
                if(!e){
                    var c = this.getEvent.caller;
                    while(c){
                        e = c.arguments[0];
                        if(e && Event==e.constructor){
                            break;
                        }
                        c = c.getEvent.caller;
                    }
                }
                return e;
            },
            preventDefault: function(event){
                if(event.preventDefault()){
                    event.preventDefault();
                }else{
                    event.returnValue = false;
                }
            }
        };  
        function EventTarget(){
            //handlers是一个对象
            this.handlers = {};
        }
        EventTarget.prototype = {
            constructor: EventTarget,
            //初始化handlers的event.type属性是一个数组,并把函数成为该数组元素
            addHandler: function(type,handler){
                //注意这里使用this
                if(typeof this.handlers[type] == "undefined"){
                    this.handlers[type] = [];
                }
                this.handlers[type].push(handler);
            },
            //执行函数,在下面调用,如果event.type属性(这是一个数组)存入了函数作为元素,取出数组中元素依次运行
            run: function(event){
                if(!event.target){
                    event.target = this;
                }
                if(this.handlers[event.type] instanceof Array){
                    var runs = this.handlers[event.type];
                    for(var i=0,len=runs.length;i<len;i++){
                        runs[i](event);
                    }
                }
            },
            //遍历某event.type属性的每个元素,找到要删除的方法后删除该元素
            removeHandler: function(type,handler){
                if(this.handlers[type] instanceof Array){
                    var remove = this.handlers[type];
                    for(var i=0,len=remove[type].length;i<len;i++){
                        if(remove[i] === handler){
                            break;
                        }
                    }
                    remove.splice(i,1);
                }
            }
        };
        //准备工作做完,开始封装一个拖拽函数,返回一个拖拽对象
        var dragdrop =function(){
            //定义一个EventTarget对象,最后作为返回值,因为EventTarget对象在上面有add和remove接口
            var dragObject = new EventTarget();
            var dragging = null,diffx = 0,diffy = 0,message;
            //针对ie5+,通用的获取浏览器长宽
            var browerWidth = document.documentElement.clientWidth || document.body.clientWidth;
            var browerHeight = document.documentElement.clientHeight || document.body.clientHeight;
            //真正实现拖拽功能的函数
            function handleEvent(event){
                event = EventUtil.getEvent(event);
                var target = EventUtil.getTarget(event);
                switch(event.type){
                    case "mousedown":
                        //如果鼠标点击的标签className中有draggable,就可以拖拽
                        if(target.className.indexOf("draggable") > -1){
                            dragging = target;
                            //diffx是偏移量,鼠标选取位置和div最左边界的距离
                            diffx = event.clientX - target.offsetLeft;
                            diffy = event.clientY - target.offsetTop;
                            message = "开始拖拽吧";
                            dragObject.run({type:"dragstart",target:dragging,mes:message});
                        }
                        break;
                    case "mousemove":
                        if(dragging !== null){
                            // 获取拖拽对象的长宽
                            if(dragging.currentStyle){      //IE不支持getComputedStyle方法
                                var draggingW = dragging.currentStyle.width;
                                var draggingH = dragging.currentStyle.height;
                            }else{                       //非IE浏览器可以用getComputedStyle方法
                                var draggingW = document.defaultView.getComputedStyle(dragging,null).width;
                                var draggingH = document.defaultView.getComputedStyle(dragging,null).height;
                            }
                            //注意这里使用(event.clientX-diffx)<0来判断是否越界,而不能用dragging.offsetLeft<0
                            //因为判断dragging.offsetLeft<0,就会在dragging.offsetLeft小于0之后再重新赋值左边距
                            //这就导致,浏览器显示了offsetLeft<0的div后才会再重新赋值为0
                             //就会得到,div一移出边界再立马紧贴边界的效果,显然很不理想
                            //然而直接判断event.clientX-diffx,offsetLeft会在鼠标移动中触发mousemove一直是0
                            if((event.clientX-diffx)<0){
                                //如果鼠标位置比之前要往左了,重置为0
                                dragging.style.left = 0 + "px";
                            }else if((event.clientX-diffx)>(browerWidth - parseInt(draggingW))){
                                //如果鼠标位置比之前要往右了,设置为最右值
                                dragging.style.left = (browerWidth - parseInt(draggingW)) + "px";
                            }else{
                                //正常情况直接设置值
                                dragging.style.left = (event.clientX - diffx) + "px";
                            }
                            if((event.clientY-diffy)<0){
                                dragging.style.top = 0 + "px";
                            }else if((event.clientY-diffy)>(browerHeight - parseInt(draggingH))){
                                dragging.style.top = (browerHeight - parseInt(draggingH)) + "px";
                            }else{
                                dragging.style.top = (event.clientY - diffy) + "px";
                            }
                            message = "正在拖动中";
                                  dragObject.run({type:"drag", target: dragging, mes:message});
                        }
                        break;
                    case "mouseup": 
                        message = "";
                        dragObject.run({type:"dragend", target: dragging, mes:message});
                        dragging = null;
                        document.onmousemove = null;  
                        document.onmouseup = null; 
                        break;
                }
            };
            //外部接口
            dragObject.enable = function(){
                    EventUtil.addHandler(document, "mousedown", handleEvent);
                    EventUtil.addHandler(document, "mousemove", handleEvent);
                    EventUtil.addHandler(document, "mouseup", handleEvent);
            };            
            dragObject.disable = function(){
                    EventUtil.removeHandler(document, "mousedown", handleEvent);
                    EventUtil.removeHandler(document, "mousemove", handleEvent);
                    EventUtil.removeHandler(document, "mouseup", handleEvent);
            };
            //返回该对象用于自定义拖拽事件
            return dragObject;
        };
        var dragObject = dragdrop();
        dragObject.enable();
        // 自定义拖拽事件,用于显示传入的message参数
        dragObject.addHandler("dragstart", function(event){
            var message = document.getElementById("message");
            message.innerHTML = event.mes;
        });
        dragObject.addHandler("drag", function(event){
            var message = document.getElementById("message");
            message.innerHTML = event.mes;
        });
        dragObject.addHandler("dragend", function(event){
            var message = document.getElementById("message");
            message.innerHTML = event.mes;
        });
    </script>
</body>
</html>

ps:注意138行,可以试着把(event.clientX-diffx)<0换成dragging.offsetLeft<0看看效果。
对拖拽代码中的具体解释:
①事件流用于兼容各浏览器
②EventTarget方法为了返回一个,外界方便操作的接口。
③ EventTarget函数内部通过switch,给拖拽元素添加onmousedown、onmousemove、onmouseup事件。
④通过将元素设置为position:absolute;再通过top和left属性实现拖拽。
⑤考虑边界情况,在函数内部的注释已十分详细。
⑥最后的dragstart、drag、dragend是元素在拖拽开始到结束时候触发的事件。
⑦返回EventTarget,为了可以给元素拖拽事件添加操作,在 EventTarget中定义这些事件流。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值