原生拖放實現

定義事件操做工具

let EventUtil = new Object;
/*此方法用來給特定對象添加事件,oTarget是指定對象,sEventType是事件類型,如click、keydown等,fnHandler是事件回調函數*/
EventUtil.addEventHandler = function (oTarget, sEventType, fnHandler) {
     // firefox狀況下
     if (oTarget.addEventListener) {
         oTarget.addEventListener(sEventType, fnHandler, false);
     }
     // IE下
     else if (oTarget.attachEvent) {
         oTarget.attachEvent("on" + sEventType, fnHandler);
     }
     else {
         oTarget["on" + sEventType] = fnHandler;
     }
};
/*此方法用來移除特定對象的特定事件,oTarget是指定對象,sEventType是事件類型,如click、keydown等,fnHandler是事件回調函數*/      
EventUtil.removeEventHandler = function (oTarget, sEventType, fnHandler) {
     if (oTarget.removeEventListener) {
         oTarget.removeEventListener(sEventType, fnHandler, false);
     } else if (oTarget.detachEvent) {
         oTarget.detachEvent("on" + sEventType, fnHandler);
     } else {
         oTarget["on" + sEventType] = null;
     }
};

/*格式化事件,由於IE和其餘瀏覽器下獲取事件的方式不一樣而且事件的屬性也不盡相同,經過此方法提供一個一致的事件*/
EventUtil.formatEvent = function (oEvent) {
     // isIE和isWin引用到一個js文件,判斷瀏覽器和操做系統類型
     if (isIE && isWin) {
         oEvent.charCode = (oEvent.type == "keypress") ? oEvent.keyCode : 0;
         // IE只支持冒泡,不支持捕獲
         oEvent.eventPhase = 2;
         oEvent.isChar = (oEvent.charCode > 0);
         oEvent.pageX = oEvent.clientX + document.body.scrollLeft;
         oEvent.pageY = oEvent.clientY + document.body.scrollTop;
         // 阻止事件的默認行爲
         oEvent.preventDefault = function () {
             this.returnValue = false;
         };

          // 將toElement,fromElement轉化爲標準的relatedTarget
         if (oEvent.type == "mouseout") {
             oEvent.relatedTarget = oEvent.toElement;
         } else if (oEvent.type == "mouseover") {
             oEvent.relatedTarget = oEvent.fromElement;
         }
         // 取消冒泡     
         oEvent.stopPropagation = function () {
             this.cancelBubble = true;
         };

         oEvent.target = oEvent.srcElement;
         // 添加事件發生時間屬性,IE沒有
         oEvent.time = (new Date).getTime();
     }
     return oEvent;
};

EventUtil.getEvent = function() {
     if (window.event) {
         // 格式化IE的事件
         return this.formatEvent(window.event);
     } else {
         return EventUtil.getEvent.caller.arguments[0];
     }
};

高級自定義事件-觀察者:

function EventTarget(){
    this.handlers = {};
}
EventTarget.prototype = {
    constructor: EventTarget,
    addHandler: function(type, handler){
        if (typeof this.handlers[type] == "undefined"){
            this.handlers[type] = [];
        }
        this.handlers[type].push(handler);
    },
    fire: function(event){
        if (!event.target){
            event.target = this;
        }
        if (this.handlers[event.type] instanceof Array){
            let handlers = this.handlers[event.type];
            for (var i=0, len=handlers.length; i < len; i++){
                handlers[i](event);
            }
        }
    },
    removeHandler: function(type, handler){
        if (this.handlers[type] instanceof Array){
            let handlers = this.handlers[type];
            for (var i=0, len=handlers.length; i < len; i++){
                if (handlers[i] === handler){
                    break;
                }
            }
            handlers.splice(i, 1);
        }
    }
};

EventTarget 類型有一個單獨的屬性 handlers ,用於儲存事件處理程序。還有三個方法:數組

addHandler() ,用於註冊給定類型事件的事件處理程序;
fire() ,用於觸發一個事件;
removeHandler() ,用於註銷某個事件類型的事件處理程序。
  1. addHandler() 方法接受兩個參數:事件類型和用於處理該事件的函數。當調用該方法時,會進行一次檢查,看看 handlers 屬性中是否已經存在一個針對該事件類型的數組;若是沒有,則建立一個新的。而後使用 push() 將該處理程序添加到數組的末尾。若是要觸發一個事件,要調用 fire() 函數。該方法接受一個單獨的參數,是一個至少包含 type屬性的對象。
  2. fire() 方法先給 event 對象設置一個 target 屬性,若是它還沒有被指定的話。而後它就查找對應該事件類型的一組處理程序,調用各個函數,並給出 event 對象。由於這些都是自定義事件,因此 event 對象上還須要的額外信息由你本身決定。
  3. removeHandler() 方法是 addHandler() 的輔助,它們接受的參數同樣:事件的類型和事件處理程序。這個方法搜索事件處理程序的數組找到要刪除的處理程序的位置。若是找到了,則使用 break操做符退出 for 循環。而後使用 splice() 方法將該項目從數組中刪除。

定義拖放函數:

let DragDrop = function(){
    let dragdrop = new EventTarget();
    let dragging = null;
    let diffx = 0;
    let diffy = 0;
    function handleEvent(event){
        // 獲取事件和目標
        event = EventUtil.getEvent(event);
        let target = EventUtil.getTarget(event);
        // 肯定事件類型
        switch(event.type){
            case "mousedown":
                if (target.className.indexOf("draggable") > -1){
                    dragging = target;
                    diffx = event.clientX - target.offsetLeft;
                    diffy = event.clientY - target.offsetTop;
                    dragdrop.fire({type:"dragstart", target: dragging, x: event.clientX, y: event.clientY});
                }
                break;
            case "mousemove":
                if (dragging !== null){
                    // 指定位置
                    dragging.style.left = (event.clientX - diffx) + "px";
                    dragging.style.top = (event.clientY - diffy) + "px";
                    // 觸發自定義事件
                    dragdrop.fire({type:"drag", target: dragging, x: event.clientX, y: event.clientY});
                }
                break;
            case "mouseup":
                dragdrop.fire({type:"dragend", target: dragging, x: event.clientX, y: event.clientY});
                dragging = null;
                break;
        }
    };
    //公共接口
    dragdrop.enable = function(){
        EventUtil.addHandler(document, "mousedown", handleEvent);
        EventUtil.addHandler(document, "mousemove", handleEvent);
        EventUtil.addHandler(document, "mouseup", handleEvent);
    };
    dragdrop.disable = function(){
        EventUtil.removeHandler(document, "mousedown", handleEvent);
        EventUtil.removeHandler(document, "mousemove", handleEvent);
        EventUtil.removeHandler(document, "mouseup", handleEvent);
    };
    return dragdrop;
}();

DragDrop對象封裝了拖放的全部基本功能。這是一個單例對象,並使用了模塊模式來隱藏某些實現細節。dragging變量起初是null,將會存放被拖動的元素,因此當該變量不爲null時,就知道正在拖動某個東西。handleEvent()函數處理拖放功能中的全部的三個鼠標事件。它首先獲取event對象和事件目標的引用。以後,用一個switch語句肯定要觸發哪一個事件樣式。當mousedown事件發生時,會檢查target的class是否包含 "draggable" 類,若是是,那麼將target存放到dragging中。這個技巧能夠很方便地經過標記語言而非JavaScript腳原本肯定可拖動的元素。瀏覽器

handleEvent()的mousemove狀況和前面的代碼同樣,不過要檢查dragging是否爲null。當它不是null,就知道dragging 就是要拖動的元素,這樣就會把它放到恰當的位置上。mouseup狀況就僅僅是將 dragging 重置爲null,讓 mousemove事件中的判斷失效。網絡

DragDrop還有兩個公共方法:enable()和disable(),它們只是相應添加和刪除全部的事件處理程序。這兩個函數提供了額外的對拖放功能的控制手段。函數

要使用DragDrop對象,只要在頁面上包含這些代碼並調用enable()。拖放會自動針對全部包含"draggable" 類的元素啓用,以下例所示:工具

<div class="draggable" style="position:absolute; background:red"> </div>

注意爲了元素能被拖放,它必須是絕對定位的。this

DragDrop.addHandler("dragstart", function(event){
    let status = document.getElementById("status");
    status.innerHTML = "Started dragging " + event.target.id;
});

DragDrop.addHandler("drag", function(event){
    let status = document.getElementById("status");
    status.innerHTML += "<br/> Dragged " + event.target.id + " to (" + event.x +"," + event.y + ")";
});

DragDrop.addHandler("dragend", function(event){
    let status = document.getElementById("status");
    status.innerHTML += "<br/> Dropped " + event.target.id + " at (" + event.x +"," + event.y + ")";
});

這段代碼定義了三個事件:dragstart、drag和dragend。它們都將被拖動的元素設置爲了target,並給出了 x 和 y 屬性來表示當前的位置。它們觸發於dragdrop對象上,以後在返回對象前給對象增長enable()和disable()方法。這些模塊模式中的細小更改令DragDrop對象支持了事件.操作系統

這裏,爲 DragDrop 對象的每一個事件添加了事件處理程序。還使用了一個元素來實現被拖動的元素當前的狀態和位置。一旦元素被放下了,就能夠看到從它一開始被拖動以後通過的全部的中間步驟。firefox

爲 DragDrop 添加自定義事件可使這個對象更健壯,它將能夠在網絡應用中處理複雜的拖放功能。prototype

相關文章
相關標籤/搜索