兼容所有瀏覽器的拖拽交互


目錄:

  1. 用H5方法實現拖拽交互
  2. 自定義事件
  3. 自定義拖拽事件
  4. 實現兼容所有瀏覽器的拖拽交互

跟着教程用H5實現了一個簡單的拖拽,左右兩個白盒子是容器,兩邊的紅盒子可以任意拖拽。

滿足以下兩個要求:

  • 被拖起來的盒子變成黃色,並且在原容器中消失,跟隨鼠標移動。
  • 被拖拽的盒子進入容器時,容器會變顏色,離開時恢復原來的顏色。
  • 拖拽釋放后,添加到准確的位置。

HTML+CSS為:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>拖動</title>
</head>
<style>
    body{
        background-color: #ccc;
    }
    .container {
        width: 200px;
        height: 800px;
        background-color: #fff;
        border: 1px solid #000;
        margin: 50px;
        float: left;
    }
    .box {
        width: 200px;
        height: 80px;
        background-color: #f00;
        border: 1px solid #000;
    }
    .light {
        background-color: #f9f;
    }
    .move {
        background-color: #ffff73;
    }
    .hide {
        display: none;
    }
</style>
<body>
<div class="wrap">
    <div class="container">
        <div class="box">1</div>
        <div class="box">2</div>
        <div class="box">3</div>
        <div class="box">4</div>
    </div><!--container結束-->
    <div class="container">
        <div class="box">5</div>
        <div class="box">6</div>
        <div class="box">7</div>
    </div><!--container結束-->
</div><!--wrap結束-->

       <script type="text/javascript" src="js/util.js"></script> 

     <script type="text/javascript" src="js/dragAnddrop.js"></script>
</body>
</html>

(util.js中是一些平時經常會用到的方法,像下面會出現的$.on( )用於接受一個選擇器,然后在其表示的節點上綁定事件。$.getElements( )負責選出符合接收到的選擇器的所有元素。)

看教程之前,我的代碼大致是這樣的:

window.onload=function(){
    var boxes=getElements(".box");
    //設置拖動目標可以被拖動
    each(me.box,function(item,index){
        item.draggable=true;
    });
    //獲取拖動目標,並為其添加樣式
    $.on(".wrap","dragstart",function(e){
        var e=e||window.event,
            target=e.target||e.srcElement;
        if (hasClass(target,"box")) {
            e.dataTransfer.setData("text/plain",".move");
        }
    });
        //其他功能
}

然后我看到了教程中這樣的代碼:

(function () {
    var dnd = {
      // 初始化
      init: function () {
        var me = this;
        me.src = document.querySelector('#demo1-src');
        me.panelList = document.querySelector('.panel-list');

        // 為拖拽源監聽dragstart,設置關聯數據
        me.src.addEventListener('dragstart', me.onDragStart, false);

        //省略
      onDragStart: function (e) {
        e.dataTransfer.setData('text/plain', 'demo1-src');
      },
      //省略
    dnd.init();
  }());

頓時覺得自己寫了一坨屎。不過還好,終於見識到了傳說中的面向對象。

 

在用H5實現拖拽交互的過程中,我遇到一個問題:

為drop添加事件處理程序的時候,要用appendChild方法將被拖拽的盒子添加到容器中去,那么要怎么獲取到被拖拽的元素呢?

我先想到可以在dragstart事件處理程序中把被拖拽的元素保存在一個變量里,然后再drop的事件處理程序訪問它。但是很遺憾,並不能訪問到。想一想,這是因為變量被聲明之后的值是undefined,他在dragstart事件處理程序中被賦值,結束之后這個值就已經失效了,所以drop的事件處理程序中訪問到的變量值是undefined。

然后我想到了,在事件處理程序中想要獲得信息,可以通過傳入的事件對象,想到這里,之前看的雲里霧里的dataTransfer對象就派上用場了。可以在dragstart事件處理程序中將為被拖拽的對象添加的class通過setData()方法傳遞給drop,這樣就可以獲取到他了。

最終寫好的代碼如下:

window.onload=function(){
    var dragdrop={
        //初始化
        init:function(){
            var me=this;
            me.box=getElements(".box");
            //設置拖動目標可以被拖動
            each(me.box,function(item,index){
                item.draggable=true;
            });
            //獲取拖動目標,並為其添加樣式
            $.on(".wrap","dragstart",me.dragStart);
            //讓拖動目標在拖起來的時候在原容器中消失
            $.on(".wrap","drag",me.Drag);
            //松開的時候恢復原狀
            $.on(".wrap","dragend",me.dragEnd);
            //監聽放置目標的dragenter事件,添加視覺效果
            $.on(".wrap","dragenter",me.dragEnter);
            //設置放置目標
            $.on(".wrap","dragover",me.prevent);
            //取消視覺效果
            $.on(".wrap","dragleave",me.dragLeave);
            //在拖放目標上處理
            $.on(".wrap","drop",me.dragDrop);
        },
        dragStart:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"box")) {
                e.dataTransfer.setData("text/plain",".move");
                addClass(target,"move");
            }
        },
        Drag:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"box")) {
                addClass(target,"hide");
            }
        },
        dragEnd:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"box")) {
                removeClass(target,"hide");
                removeClass(target,"move");
            }
        },
        dragEnter:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"container")) {
                addClass(target,"light");
            }
        },
        prevent:function(e){
            $.prevent(e);
        },
        dragLeave:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"container")) {
                removeClass(target,"light");
            }
        },
        dragDrop:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"container")) {
                var dragging=$(e.dataTransfer.getData("text/plain"));
                removeClass(dragging,"move");
                removeClass(target,"light");
                target.appendChild(dragging);
            }
        },
    }
    dragdrop.init();
}

雖然還沒有很深刻的體會到這種面向對象寫法的好處,但還是模仿着寫了。

 

接下來就要開始照顧不支持H5的寶寶們了。

一開始就被一系列的問題卡住,他怎么動起來?什么時候讓他動起來?怎么讓他跟着鼠標走?他怎么回去?怎么給它綁定事件處理程序?

前兩個問題還能勉強想出來,當鼠標按下的時候,可以給他設置position:absolute讓他動起來,然后讓他的left和top和mousedown事件的鼠標坐標相同就可以了。

后幾個問題就真的束手無策了。

在《JavaScript高級程序設計》中找到答案,發現這件事情真的沒那么簡單。

最本質的,要深刻地理解事件是怎么回事。

事件是一種叫做觀察者的設計模式,這是一種創建松散耦合代碼的技術。對象可以發布事件,用來表示在該對象生命周期中某個有趣的時刻到了。然后其他對象可以觀察該對象,等待這些有趣的時刻到來並通過運行代碼來響應。

意思就是說,事件是一個對象發出的信號,他邀請別人一起來慶祝他重要的時刻,不管有沒有人願意來,他都可以發出信號。

那么,他要怎樣發出信號呢? 通過 函數調用 很容易就可以實現對象之間的通信。

有行為就需要有管理機制,得到一個名字,檢測其對應的函數,執行函數。所以就需要一個對象來進行管理。

函數的調用需要一些代碼,需要函數名。所以這個對象中應該保存着函數名表示事件類型,還要保存該類型相應的代碼,所以他還需要一個個方法為其添加事件類型和處理函數,還要有一個讓函數中的代碼執行的方法,這個方法讀取函數名,然后執行這個函數名對應的代碼。可以添加就要可以刪除,所以還需要一個負責刪除處理程序的方法。

所以,這個對象應該長這樣:

{

事件類型和他的代碼們:

{type1:[fun1,fun2...]

  type2:[fun1,fun2,..]

  .....}

接待員:為對象添加type和相應fun。

執行官:接收一個type執行相應代碼。

清理員:接收一個type和他想刪除的fun,為其刪掉這個fun。

}

 用JavaScript實現就長下面這樣:

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(e){
        var handlers=this.handlers[e.type]
        if (isArray(handlers)) {
            for (var i = 0,len = handlers.length;i<len; i++) {
                handlers[i](e);
            }
        }
    },

    removeHandler:function(type,handler){
        if (isArray(this.handlers[type])) {
            removeItem(this.handlers[type],handler);
        }
    },
};

用這種構造函數+原型的方式,就是為了自定義的事件可以繼承他的方法,並擁有自己的事件類型。

 

有了這個完美的事件管理機制,就可以自定義拖拽事件對象了。

按照H5的思路,他應該可以觸發這6種事件:dragstart、drag、dragend、dragenter、dragleave和drop。

首先,他如何觸發dragstart?什么時候應該觸發dragstart?

拖拽的本質就是,鼠標在一個元素上按下不松開,挪動鼠標,放到另一個地方,松開。

聽起來好像很熟悉,就是我們熟悉的mousedown、mousemove和mouseup嘛。

所以,當元素上發生mousedown的時候,就觸發dragstart事件,移動的時候觸發drag,移動到目標元素里面的時候觸發dragenter,移出去的時候觸發dragleave,松開的時候要是在目標元素里面就觸發drop,然后觸發dragend(因為不在里面的話要回到原位)。

如果要綁定事件的話,就要知道綁定在哪里,因為事先不知道要被綁定的元素,所以綁定在document上,讓每個DOM對象都有機會成為被拖拽的和放置目標。然后指定篩選條件,在指定的滿足條件的對象上面觸發事件。

那么,如何指定被拖拽對象和放置目標呢?

還是模仿H5,他通過設置draggable屬性和阻止瀏覽器默認行為,我們也可以通過添加一個自定義屬性或者類名,來判別目標對象。

想明白這些之后,就可以開始寫處理程序了,解決了重重問題之后,最后的對象是這樣的:

//自定義拖動事件(和HTML的class屬性耦合)
var DragDrop=function(){
    var dragdrop=new eventTarget(),
        dragging=null,  
        x=0,y=0,
        border_r,border_l,border_top,border_btm,
        left,right,top,bottom;
    
    function handle(e){
        var e=e||window.event,
            target=e.target||e.srcElement,
            drops=getElements(".dropable");  //問題1
        switch(e.type){
            case "mousedown":
                if (hasClass(target,"draggable")) {
                    dragging=target;
                    x=e.clientX-target.offsetLeft;
                    y=e.clientY-target.offsetTop;
                    dragdrop.fire({
                        type:"dragstart",
                        target:dragging,
                        x:e.clientX,
                        y:e.clientY
                    });
                }
                break;
            case "mousemove":
                if (dragging!==null) {
                    dragging.style.left=e.clientX-x+"px";
                    dragging.style.top=e.clientY-y+"px";
                    dragdrop.fire({
                        type:"drag",
                        target:dragging,
                        x:e.clientX,
                        y:e.clientY
                    });
                    if (drops[0]) {
                        for (var i = 0; i < drops.length; i++) {
                        border_l=dragging.offsetLeft;
                        border_r=parseInt(getCSS(dragging,"width"))+border_l;  //問題2
                        border_top=dragging.offsetTop;
                        border_btm=parseInt(getCSS(dragging,"height"))+border_top;
                        left=drops[i].offsetLeft;
                        right=parseInt(getCSS(drops[i],"width"))+left;
                        top=drops[i].offsetTop;
                        bottom=parseInt(getCSS(drops[i],"height"))+top;
                        if(border_r>left&&border_l<right&&border_top<bottom&&border_btm>top){
                            dragdrop.fire({
                                type:"dragenter",
                                target:drops[i]
                            });
                            }else{
                                dragdrop.fire({
                                    type:"dragleave",
                                    target:drops[i]
                                });
                            }
                        }
                    }
                }
                break;
            case "mouseup":
                if (drops[0]&&dragging) {
                    for (var i = 0; i < drops.length; i++) {
                        dragWidth=parseInt(getCSS(dragging,"width"));
                        border_r=dragWidth+dragging.offsetLeft;
                        border_l=dragging.offsetLeft;
                        left=drops[i].offsetLeft;
                        right=drops[i].offsetLeft+parseInt(getCSS(drops[i],"width"));
                        if(border_r>left&&border_l<right&&border_top<bottom&&border_btm>top){  //問題3
                            dragdrop.fire({
                                type:"drop",
                                target:drops[i],
                                dragging:dragging
                            });
                        break;
                        }
                    }
                }
                dragdrop.fire({
                    type:"dragend",
                    target:dragging,
                    x:e.clientX,
                    y:e.clientY
                });
                dragging=null;
                break;
        }
    };
    dragdrop.enable=function(){
        $.add(document,"mousedown",handle);
        $.add(document,"mousemove",handle);
        $.add(document,"mouseup",handle);
    };
    dragdrop.disable=function(){
        $.remove(document,"mousedown",handle);
        $.remove(document,"mousemove",handle);
        $.remove(document,"mouseup",handle);
    };
    return dragdrop;
}

問題1:

最開始的時候,我是不知道該怎么將被拖拽對象和目標對象分開的,我一直想,當感受到拖拽的時候就讓事件的target變成被拖拽的元素,當感受到有東西改過來時讓事件的target變成目標對象,當我嘗試着把dragenter事件讓mouseenter來觸發時,失望的發現,鼠標上拖着東西的時候是無法觸發mouseenter的。

然后我想到,既然只能獲得被拖拽的對象,也沒有想mousedown那樣的事件可以來獲取放置目標,那就換個思路。通過class來獲取,畢竟在我心中class是個萬能的東西。所以我的邏輯就是,獲取到所有class=droppable的對象,判斷他們是否被拖動元素覆蓋,是的話就觸發dragenter,不是的話就觸發dragleave。(這也就導致了問題3。)

問題2:

我想到的判斷拖拽元素是否在目標元素上方的方法是,看被拖動元素是否有兩條邊線在目標元素之內。這就需要獲取拖拽元素和目標元素的width。

而用node.style.width獲取不到。查了一下發現,這個方法只能獲取到寫在標簽里面的樣式。想要獲取CSS中的樣式,需要用DOM2級樣式中定義的document.defaultView.getComputerStyle()和IE的obj.currentStyle。所以就寫了一個getCSS方法,代碼如下:

//獲得元素的CSS樣式
function getCSS(ele,name){
    if (ele) {
        return document.defaultView.getComputedStyle?
            document.defaultView.getComputedStyle(ele,null)[name]:ele.currentStyle[name];
    }
}

問題3:

由於我上面判斷目標對象的邏輯,被拖拽的元素會更容易drop進第一個帶有droppable的容器里面。這個問題還有待解決。

 

定義好拖拽事件對象之后,就可以為其綁定處理程序了。

這時候就體現出之前面向對象寫法的好處了:①他將所有事件處理函數都變成對象自身的方法,減少了很多游離再全局中的變量。②這些方法只有他自己能調用,外部不需要知道這里會發生什么,只需要知道他能做這些事情就夠了。

這樣的寫法,讓一切變得非常井然有序,邏輯清晰。

因為他們處理的方法是不同的,所以事件處理程序中的執行代碼會有些差異,需要判斷一下,不然會有沖突。

到這里,已經可以實現我想要的功能了,而且還沒有發現什么重大的問題。所以暫時先寫成這個樣子啦。

雖然一直很嫌棄不支持新方法的瀏覽器,尤其是IE。但是這次要好好的感謝一下他,讓我更深刻的了解了事件,並且讓我見識到了JavaScript如此強大的函數,它可以創造無限可能。

完整代碼(https://github.com/mamengyi/Baidu/tree/master/2015_Spring/task0002/%E6%8B%96%E6%8B%BD%E4%BA%A4%E4%BA%92)

//自定義事件
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(e){
        var handlers=this.handlers[e.type]
        if (isArray(handlers)) {
            for (var i = 0,len = handlers.length;i<len; i++) {
                handlers[i](e);
            }
        }
    },

    removeHandler:function(type,handler){
        if (isArray(this.handlers[type])) {
            removeItem(this.handlers[type],handler);
        }
    },
};


//自定義拖動事件(和HTML的class屬性耦合)
var DragDrop=function(){
    var dragdrop=new eventTarget(),
        dragging=null,
        x=0,y=0,
        border_r,border_l,border_top,border_btm,
        left,right,top,bottom;
    
    function handle(e){
        var e=e||window.event,
            target=e.target||e.srcElement,
            drops=getElements(".dropable");
        switch(e.type){
            case "mousedown":
                if (hasClass(target,"draggable")) {
                    dragging=target;
                    x=e.clientX-target.offsetLeft;
                    y=e.clientY-target.offsetTop;
                    dragdrop.fire({
                        type:"dragstart",
                        target:dragging,
                        x:e.clientX,
                        y:e.clientY
                    });
                }
                break;
            case "mousemove":
                if (dragging!==null) {
                    dragging.style.left=e.clientX-x+"px";
                    dragging.style.top=e.clientY-y+"px";
                    dragdrop.fire({
                        type:"drag",
                        target:dragging,
                        x:e.clientX,
                        y:e.clientY
                    });
                    if (drops[0]) {
                        for (var i = 0; i < drops.length; i++) {
                        border_l=dragging.offsetLeft;
                        border_r=parseInt(getCSS(dragging,"width"))+border_l;
                        border_top=dragging.offsetTop;
                        border_btm=parseInt(getCSS(dragging,"height"))+border_top;
                        left=drops[i].offsetLeft;
                        right=parseInt(getCSS(drops[i],"width"))+left;
                        top=drops[i].offsetTop;
                        bottom=parseInt(getCSS(drops[i],"height"))+top;
                        if(border_r>left&&border_l<right&&border_top<bottom&&border_btm>top){
                            dragdrop.fire({
                                type:"dragenter",
                                target:drops[i]
                            });
                            }else{
                                dragdrop.fire({
                                    type:"dragleave",
                                    target:drops[i]
                                });
                            }
                        }
                    }
                }
                break;
            case "mouseup":
                if (drops[0]&&dragging) {
                    for (var i = 0; i < drops.length; i++) {
                        dragWidth=parseInt(getCSS(dragging,"width"));
                        border_r=dragWidth+dragging.offsetLeft;
                        border_l=dragging.offsetLeft;
                        left=drops[i].offsetLeft;
                        right=drops[i].offsetLeft+parseInt(getCSS(drops[i],"width"));
                        if(border_r>left&&border_l<right&&border_top<bottom&&border_btm>top){  //會更容易drop進第一個盒子里。
                            dragdrop.fire({
                                type:"drop",
                                target:drops[i],
                                dragging:dragging
                            });
                        break;
                        }
                    }
                }
                dragdrop.fire({
                    type:"dragend",
                    target:dragging,
                    x:e.clientX,
                    y:e.clientY
                });
                dragging=null;
                break;
        }
    };
    dragdrop.enable=function(){
        $.add(document,"mousedown",handle);
        $.add(document,"mousemove",handle);
        $.add(document,"mouseup",handle);
    };
    dragdrop.disable=function(){
        $.remove(document,"mousedown",handle);
        $.remove(document,"mousemove",handle);
        $.remove(document,"mouseup",handle);
    };
    return dragdrop;
}



//拖動
window.onload=function(){
    var dragdrop={
        //初始化
        inith5:function(){
            var me=this;
            me.box=getElements(".box");
            //設置拖動目標可以被拖動
            each(me.box,function(item,index){
                item.draggable=true;
            });
            //獲取拖動目標,並為其添加樣式
            $.on(".wrap","dragstart",me.dragStart);
            //讓拖動目標在拖起來的時候在原容器中消失
            $.on(".wrap","drag",me.Drag);
            //松開的時候恢復原狀
            $.on(".wrap","dragend",me.dragEnd);
            //監聽放置目標的dragenter事件,添加視覺效果
            $.on(".wrap","dragenter",me.dragEnter);
            //設置放置目標
            $.on(".wrap","dragover",me.prevent);
            //取消視覺效果
            $.on(".wrap","dragleave",me.dragLeave);
            //在拖放目標上處理
            $.on(".wrap","drop",me.dragDrop);
        },
        init:function(){
            var me=this,
                dragevent=new DragDrop();
            dragevent.enable();
            //設置拖動目標可以被拖動
            me.box=getElements(".box");
            each(me.box,function(item,index){
                addClass(item,"draggable");
            });
            //獲取拖動目標,並為其添加樣式
            dragevent.addHandler("dragstart",me.dragStart);
            //松開的時候恢復原狀
            dragevent.addHandler("dragend",me.dragEnd);
            //監聽放置目標的dragenter事件,添加視覺效果
            dragevent.addHandler("dragenter",me.dragEnter);
            //設置放置目標
            me.container=getElements(".container");
            each(me.container,function(item,index){
                addClass(item,"dropable");
            });
            //取消視覺效果
            dragevent.addHandler("dragleave",me.dragLeave);
            //在拖放目標上處理
            dragevent.addHandler("drop",me.dragDrop);
        },
        dragStart:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"box")) {
                if (e.dataTransfer) {
                    e.dataTransfer.setData("text/plain",".move");
                    addClass(target,"move");
                }else{
                    addClass(target,"moving");
                }
            }
        },
        Drag:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (e.dataTransfer&&hasClass(target,"box")) {
                addClass(target,"hide");
            }
        },
        dragEnd:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (e.dataTransfer&&hasClass(target,"box")) {
                removeClass(target,"hide");
                removeClass(target,"move");
            }else{
                removeClass(target,"moving");
            }
        },
        dragEnter:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"container")) {
                addClass(target,"light");
            }
        },
        prevent:function(e){
            $.prevent(e);
        },
        dragLeave:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (hasClass(target,"container")) {
                removeClass(target,"light");
            }
        },
        dragDrop:function(e){
            var e=e||window.event,
                target=e.target||e.srcElement;
            if (e.dataTransfer&&hasClass(target,"container")) {
                var dragging=$(e.dataTransfer.getData("text/plain"));
                removeClass(dragging,"move");
                removeClass(target,"light");
                target.appendChild(dragging);
            }else if (e.dragging) {
                var lights=getElements(".light"),
                    len=lights.length;
                for (var i = 0; i < len; i++) {
                    removeClass(lights[0],"light"); //注意這里是一個NodeList
                }
                removeClass(e.dragging,"moving");
                target.appendChild(e.dragging);
            }
        },
    }
    if(supportDrag()){
        dragdrop.inith5();
    }else{
        dragdrop.init();
    }
}

util:

//對數組(類數組對象)的每項都執行fn(item,index)
function each(arr,fn){
    for(var i = 0 , len = arr.length ; i<len ; i++){
        fn(arr[i],i);
    }
}
//DOM元素選擇器
function getElements(selector){
    //類選擇器,返回全部項
    if(/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/.test(selector)){
        if(document.getElementsByClassName){
            return document.getElementsByClassName(selector.slice(1,selector.length));
        }
        var nodes = document.all ? document.all : document.getElementsByTagName('*');
        var arr=[];//用來保存符合的className;    
        for(var i=0;i<nodes.length;i++){
            if(hasClass(nodes[i],selector.slice(1,selector.length))){
                arr.push(nodes[i]);
            }
        }
        return arr;
    }

    //ID選擇器
    if(/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/.test(selector)){
        return document.getElementById(selector.slice(1,selector.length));
    }


    //tag選擇器
    if(/^((?:[\w\u00c0-\uFFFF\-]|\\.)+)/.test(selector)){
        return document.getElementsByTagName(selector);
    }

    //屬性選擇器
    if(/^\[[A-Za-z0-9_-\S]+\]$/.test(selector)){
        selector = selector.slice(1,selector.length-1);
        var eles = document.getElementsByTagName("*");
        selector = selector.split("=");
        var att = selector[0];
        var value = selector[1];
        var arr = []; 
        if (value) {
            for (var i = 0; i < eles.length; i++) {
                if(eles[i].getAttribute(att)==value){
                   arr.push(eles[i]);
                } 
            }
        }else{
            for (var i = 0; i < eles.length; i++) {
                if(eles[i].getAttribute(att)){
                    arr.push(eles[i]);
                } 
            }
        }
        return arr;
    }
}
function $(selector){
    var all=selector.split(/\s+/);
    var result = [],rooot=[document];
    for (var i = 0; i < all.length; i++) {
        var type=all[i][0];
        switch(type){
        //ID
        case "#" :
            for (var j = 0; j < rooot.length; j++) {
                var ele=rooot[j].getElementById(all[i].slice(1));
                if (ele) {
                    result.push(ele);
                }
            }
            break;
        
        //class
        case ".":
            for (var j = 0; j < rooot.length; j++) {
                if (document.getElementsByClassName) {
                    var eles=rooot[j].getElementsByClassName(all[i].slice(1));
                    if (eles) {
                        result=result.concat(Array.prototype.slice.call(eles));
                    }
                }else{
                    var arr = rooot[j].getElementsByTagName("*");
                    for (var i = 0; i < arr.length; i++) {
                        if (hasClass(arr[i], className)) {
                            result.push(arr[i]);
                        }
                    }
                }
            }
            break;
        //屬性
        case "[":
            var att = all[i].slice(1,all[i].length-1).split("=");
            var key = att[0],value=att[1];
            for (var j = 0; j < rooot.length; j++) {
                var eles=rooot[j].getElementsByTagName("*");
                for (var i = 0; i < eles.length; i++) {
                    if (value) {
                        for (var i = 0; i < eles.length; i++) {
                            if(eles[i].getAttribute(key)==value){
                                result.push(eles[i]);
                            }
                        }
                    }else{
                        for (var i = 0; i < eles.length; i++) {
                            if(eles[i].getAttribute(key)){
                                result.push(eles[i]);
                            }
                        }
                    }
                }
            }
            break;
        //tag
        default:
            for (var j = 0; j < rooot.length; j++) {
                eles=rooot[j].getElementsByTagName(all[i]);
                if (eles) {
                    result=result.concat(Array.prototype.slice.call(eles));
                }
            }
        }
        rooot=result;
        result=[];   
    }
    return rooot[0];
}
//檢查ele是否有className
function hasClass(ele,className){
    if (ele&&ele.className) {
        var classes=ele.className.split(/\s+/);//這里必須要切成數組之后再判斷
        if(classes.indexOf(className)!=-1){
            return true;
        } 
    }
    return false;
}

// 為element增加一個樣式名為newClassName的新樣式
function addClass(ele,newClass){
    if (!hasClass(ele,newClass)) {
        ele.className=ele.className?[ele.className,newClass].join(" "):newClass;
    }
}

// 移除element中的樣式oldClassName
function removeClass(ele,oldClass){
    if (hasClass(ele,oldClass)) {
        var arr = ele.className.split(/\s+/);
        for (var i = 0; i < arr.length; i++) {
            if(arr[i]===oldClass){
                arr.splice(i,1);
                break;
            }
        }
        ele.className=arr.join(" ");
    }
}
//獲得元素的CSS樣式
function getCSS(ele,name){
    if (ele) {
        return document.defaultView.getComputedStyle?
            document.defaultView.getComputedStyle(ele,null)[name]:ele.currentStyle[name];
    }
}
//添加事件
function addEvent(selector,event,listener){
    var ele=$(selector);
    if(ele.addEventListener){
        ele.addEventListener(event,listener,false);
    }else{
        ele.attachEvent("on"+event,listener);
    }
}
//添加事件(給節點)
function addEventToNode(ele,event,listener){
    if(ele.addEventListener){
        ele.addEventListener(event,listener,false);
    }else{
        ele.attachEvent("on"+event,listener);
    }
}
//移除事件
function removeEvent(selector,event,listener){
    var ele=$(selector);
    if(ele.removeEventListener){
        ele.removeEventListener(event,listener,false);
    }else{
        ele.detachEvent("on"+event,listener);
    }
}
function removeNodeEvent(ele,event,listener){
    if(ele.removeEventListener){
        ele.removeEventListener(event,listener,false);
    }else{
        ele.detachEvent("on"+event,listener);
    }
}

//阻止瀏覽器默認行為
function preventDefault(e){
if (e&&e.preventDefault) {
e.preventDefault();
}else{
e.returnValue=false;
}
return false;
}
$.prevent=preventDefault;

$.on = addEvent;
$.add = addEventToNode;
$.un = removeEvent;
$.remove=removeNodeEvent;

//判斷是否支持ondrag事件

function supportDrag(){ 
    var div = document.createElement('div'); 
    return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div); 
}

參考:

教程:http://qiudeqing.com/html5/2015/05/17/drag-and-drop.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM