原生js實現拖拽考慮瀏覽器窗口邊界 詳細版代碼+註釋

關於拖拽元素,在網上有不少例子,但都比較簡單,且大多數沒有徹底考慮瀏覽器窗口邊界。
參考多方資料,本身寫了個較完善的封裝拖拽的方法。
先貼出代碼,複製後運行看下效果,
全部理解在註釋中較詳細說明。javascript

<!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中定義這些事件流。css

相關文章
相關標籤/搜索