js拖拽效果 javascript实现将元素拖拽如某容器效果demo

拖拽效果demo以及文档说明

Demo效果

View Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<script type="text/javascript">
/*************************************************c插件定义部分******************************************************/
var Drag = (function () {
    var opts={container:"",drag_items:"",state:"outer" ,setContainerStyle:true ,moveInto:function(){},moveOut:function(){},endInto:function(){}},//默认参数,全局的  state:inner outer center 标示拖拽对象是碰触目标触发还是完全进入目标再触发
        curDrag,
        //以下为私有方法
        getPosition=function(elem){
            if ( !elem ) return {left:0, top:0};  
            var top = 0, left = 0;  
            if ( "getBoundingClientRect" in document.documentElement ){  
                //jquery方法  
                var box = elem.getBoundingClientRect(),   
                doc = elem.ownerDocument,   
                body = doc.body,   
                docElem = doc.documentElement,  
                clientTop = docElem.clientTop || body.clientTop || 0,   
                clientLeft = docElem.clientLeft || body.clientLeft || 0,  
                top  = box.top  + (self.pageYOffset || docElem && docElem.scrollTop  || body.scrollTop ) - clientTop,  
                left = box.left + (self.pageXOffset || docElem && docElem.scrollLeft || body.scrollLeft) - clientLeft;  
            }else{  
                do{  
                    top += elem.offsetTop || 0;  
                    left += elem.offsetLeft || 0;  
                    elem = elem.offsetParent;  
                } while (elem);  
          }  
            return {left:left, top:top};      
        },
        
        isJoinToBox=function(curDrag){
            var extendX,
                extendY,
                drag_left = parseInt(curDrag.style.left),
                drag_top = parseInt(curDrag.style.top),
                box_position = getPosition(opts.container),
                box_top = box_position.top,
                box_left = box_position.left,
                box_bottom = opts.container.offsetHeight + box_top,
                box_right = opts.container.offsetWidth + box_left;
                
                
            switch(opts.state)
            {
                case "outer":
                            extendX = curDrag.offsetWidth;
                            extendY = curDrag.offsetHeight;                           
                            break;
                case "inner":
                            extendX=0;
                            extendY=0;
                            break;
                case "center":
                            extendX = parseInt(curDrag.offsetWidth/2);
                            extendY = parseInt(curDrag.offsetHeight/2);
                            break;
                default:
                            extendX = curDrag.offsetWidth;
                            extendY = curDrag.offsetHeight;
                            
            }
            return ((drag_left >= box_left-extendX && drag_left <= box_right) &&
                    (drag_top >= box_top-extendY && drag_top <= box_bottom));
        },
        
        start=function(e){
            window.getSelection().removeAllRanges();
            var oldNode = e.target,
                newNode,
                top = getPosition(oldNode).top,
                left = getPosition(oldNode).left;
                
            if("true" === oldNode.getAttribute("drag"))
            {
                newNode = oldNode.cloneNode(true);
                newNode.style.cssText = "z-index:1;background:red;position:absolute; top:"+top+"px;left:"+left+"px";
                oldNode.parentNode.appendChild(newNode);
                curDrag = newNode;
            }
        },
        
        move=function(e){
            window.getSelection ? window.getSelection().removeAllRanges():document.selection.empty();
            if(curDrag){

                curDrag.style.top = e.pageY + "px";
                curDrag.style.left = e.pageX + "px";
                if(opts.setContainerStyle){
                    if(isJoinToBox(curDrag))
                    {
                        opts.moveInto();
                    }
                    else{
                        opts.moveOut();
                    }
                }
            }
        },
        
        end=function(e){
            window.getSelection ? window.getSelection().removeAllRanges():document.selection.empty();
            if(curDrag){
                if(isJoinToBox(curDrag)){
                    opts.endInto(curDrag.cloneNode(true));
                }
                    
                if(opts.setContainerStyle){
                    opts.moveOut();
                }
                curDrag.parentNode.removeChild(curDrag);
                curDrag = null;
            }
        },
        
        touchHandler=function(event){        
            var touches=event.changedTouches,
                touchObj=touches[0],
                type,
                simulatedEvent ;
                
            switch(event.type){
                case "touchstart":
                     type="mousedown";
                     break;
                case "touchmove":
                     type="mousemove";
                     break;     
                case "touchend":
                     type="mouseup";
                     break;
                default:
                     return;
            
            }
            simulatedEvent=document.createEvent("MouseEvent");
            simulatedEvent.initMouseEventype(type, true, true, window, 1,
                                            first.screenX,first.screenY,
                                            first.clientX, first.clientY,
                                            false,false, false, false, 0, null); 
            
            touchObj.target.dispathEvent(simulatedEvent);
            event.preventDefault();
        },
        
        init=function (option) { 
            for(prop in option) opts[prop]=option[prop];
            //alert(opts.container);
            //初始化事件
                //如果是移动设备,我们就用事件模拟触发鼠标事件
                opts.drag_items.addEventListener("touchstart", touchHandler, true);
                opts.drag_items.addEventListener("touchmove", touchHandler, true);
                opts.drag_items.addEventListener("touchend", touchHandler, true);
            //监听鼠标事件
            opts.drag_items.addEventListener("mousedown", start, true);
            document.addEventListener("mousemove", move, true);
            document.addEventListener("mouseup", end, true);
               
        };
    // 暴露公开的成员
    return{
        init: init        
    }
}());





</script>
</head>
<style type="text/css">
span{display:block; width:50px; height:50px; background:#dedede; border:1px solid #333; float:left}
#container{margin:auto;padding:10px;background:#dedede; width:380px; height:150px; border:5px solid #777; }
#container li{width:50px; height:50px; border:1px dashed #777; float:left; margin:10px 0 0 10px; list-style-type:none;}
#dragItems{margin:auto; width:480px;}
</style>
<body style="text-align:center;-webkit-user-select:none;">
将以下小方格拖入上面的容器内:<br />
注: id="dragItems"下面的子元素设置 drag="true"属性的才可以拖动<br /><br /><br />
<ul id="container">
    <li></li><li></li><li></li><li></li><li></li><li></li>
    <li></li><li></li><li></li><li></li><li></li><li></li>
</ul>
<br />
<br />
<br />
<br />
<div id="dragItems"><span drag="true">111</span><span drag="true">222</span><span drag="true">333</span><span drag="true">444</span><span drag="true">555</span><span drag="true">666</span><span drag="true">777</span><span drag="true">888</span><span drag="true">999</span></div>


</body>
</html>
<script type="text/javascript">

//*******************************************************调用部分**************************************************

var c=document.getElementById("container"),//需要拖入的容器id
    d=document.getElementById("dragItems");//被拖动的元素列表
Drag.init({
    container : c,
    drag_items : d ,
    state : "outer",//可以设置层outer(拖动元素外边挨着容器就触发),inner(拖动元素全部进入容器才触发),center (拖动元素一半进入容器时触发) 
    moveInto : function(){//当元素被拖入容器内时触发(mousemove时鼠标左键按下状态)
        c.style.border = "5px dashed #ccc";
    },
    moveOut : function(){//当元素被拖出容器时触发(mousemove时鼠标左键按下状态)
        c.style.border = "5px solid #777";
    },
    endInto : function(dragEle){//当元素被拖进容器后触发(mouseup 鼠标抬起)
        var elems=c.getElementsByTagName("li");
        for(var i=0,len=elems.length; i<len ;i++)
        {
            if(elems[i].innerHTML == "")
            {
                elems[i].appendChild(dragEle);
                dragEle.style.position="";
                break;
            }
            if(i == len-1) alert("fulled");
        }
    }
});

</script>

JavaScript 拖放效果相关技术说明

 

【触发对象】

 

触发对象是用来触发拖放程序的,程序中通过Handle属性设置。有的时候不需要整个拖放对象都用来触发,这时就需要触发对象了。
使用了触发对象后,进行移动的还是拖放对象,只是用触发对象来触发拖放(一般的使用是把触发对象放到拖放对象里面)。
ps:触发对象的另一个用法是通过设置相同的Handle,实现一个触发对象同时拖放多个拖放对象。

 

【范围限制】

 

要设置范围限制必须先把Limit设为true。范围限制分两种,分别是固定范围和容器范围限制,主要在Move程序中设置。
原理是当比较的值超过范围时,修正lefttop要设置的值使拖放对象能保持在设置的范围内。

 

【取消默认动作】

 

对选择状态的文本内容、连接和图片等进行拖放操作会触发系统的默认动作,例如ie中拖动图片鼠标会变成禁止操作状态,这样会导致这个拖放程序执行失败。

 

不过ie在设置了setCapture之后,通过用户界面用鼠标进行拖放操作和内容选择都会被禁止。
意思就是setCapture之后就不能对文档内容进行拖放和选择,注意这里的拖放是指系统的默认动作,例如ondragstart就不会被触发。
不过如果setCapture的参数是false的话,容器内的对象还是可以触发事件的(具体看鼠标捕获部分),所以setCapture的参数要设成true或保留默认值。

 

ff的鼠标捕获没有这个功能,但可以用preventDefault来取消事件的默认动作来解决:

 

oEvent.preventDefault();

 


ps:据说使用preventDefault会出现mouseup丢失的情况,但我在ff3中测试没有发现,如果各位发现任何mouseup丢失的情况,务必告诉我啊。

 

【清除选择】

 

ie在设置setCapture之后内容选择都会被禁止,但也因此不会清除在设置之前就已经选择的内容,而且设置之后也能通过其他方式选择内容,
例如用ctrl+a来选择内容。
psonkeydownonkeyuponkeypress事件不会受到鼠标捕获影响。
ffmousedown时就能清除原来选择的内容,但拖动鼠标,ctrl+a时还是会继续选择内容。
不过在取消了系统默认动作之后,这样的选择并不会对拖放操作造成影响,这里设置主要还是为了更好的体验。

 

以前我用禁止拖放对象被选择的方法来达到目的,即ie中设置拖放对象的onselectstart返回false,在ff中设置样式MozUserSelect(css:-moz-user-select)none
但这种方法只能禁止拖放对象本身被选择,后来找到个更好的方法清除选择,不但不影响拖放对象的选择效果,还能对整个文档进行清除:
ie:document.selection.empty()
ff:window.getSelection().removeAllRanges()
为了防止在拖放过程中选择内容,所以把它放到Move程序中,下面是兼容的写法:

 

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

 


这里说说ff下有一个比较奇怪的现象,在SimpleDrag中第一次拖放没有问题,但第二次拖放时鼠标就会显示禁止操作的样式(拖动失败)。
我发现如果在Start程序中阻止默认动作(preventDefault)或清除选择(window.getSelection().removeAllRanges())就能正常了。
所以我估计是在点击事件后,拖放对象已经默认设置成选择状态了(虽然看起来不是),再拖动时就触发了系统的默认拖动事件。
为了支持推断,我在html插入一段文本,然后在每一次拖放之后选择一下那段文本(间接取消拖放对象的选择),那就能正常了。
不过暂时还没找到官方说明,所以还不能下结论。

 

posted @ 2012-11-07 17:42  CatherineGao  阅读(1483)  评论(1编辑  收藏  举报