兼容所有浏览器的拖拽交互

目录:

  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

posted @ 2016-09-25 21:53  马梦一  阅读(4958)  评论(0编辑  收藏  举报