SitePoint Sponsor

User Tag List

Results 1 to 5 of 5
  1. #1
    SitePoint Zealot
    Join Date
    Jul 2006
    Posts
    151
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Dragging between lists

    So I've been working on a script to allow the user to drag items between lists because of an enhancement to a CMS I want to create.

    I've got the script working to my satisfaction in Opera 9, Netscape 8, and Firefox 2.0, and partially failing in Internet Explorer (all versions). Obviously I need to find out how to break my script to make it work in IE.

    The script is available to view at:

    http://www.unitorganizer.com/sortable/

    The relevant code is below:

    HTML 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=iso-8859-1" />
    <title>Dragging between lists</title>
    <script type="text/javascript" src="js/dom-drag.js"></script>
    <script type="text/javascript" src="js/sortable.js"></script>
    <style type="text/css">
    ul.sortable li {
    	position: relative;
    }
    
    ul.boxy {
    	list-style-type: none;
    	padding: 0px;
    	margin: 0px;
    	width: 150px;
    	font-size: 13px;
    	font-family: Arial, sans-serif;
    	background-color: #fff;
    }
    
    ul.boxy li {
    	cursor:move;
    	padding: 2px 2px;
    	margin: 2px;
    	margin-left: 20px;
    	border: 1px solid black;
    	background-color: #eee;
    }
    
    .clickable a {
    	display: block;
    	text-decoration: none;
    	cursor: pointer;
    	cursor: hand;
    }
    
    .clickable li:hover {
    	background-color: #f6f6f6;
    }
    
    .handle {
    	background-color: #0033FF;
        width: 50px;
    	height: 15px;
    	display: block;
    	float: left;
    	margin-right: 30px;
    }
    #debug {
    	position: absolute;
    	left: 500px;
    	top: 100px;
    }
    </style>
    </head>
    
    <body>
    <ul id="list1" class="boxy sortable">
    	<li id="item1"><span id="handle1" class="handle">&nbsp;</span>One</li>
    	<li id="item2"><span id="handle2" class="handle">&nbsp;</span>Two
    		<ul class="boxy sortable" id="list2">
    			<li id="item3"><span id="handle3" class="handle">&nbsp;</span>i</li>
    			<li id="item4"><span id="handle4" class="handle">&nbsp;</span>ii</li>
    			<li id="item5"><span id="handle5" class="handle">&nbsp;</span>iii</li>
    			<li id="item6"><span id="handle6" class="handle">&nbsp;</span>iv</li>
    			<li id="item7"><span id="handle7" class="handle">&nbsp;</span>v
    				<ul class="boxy sortable" id="list3">
    					<li id="item8"><span id="handle8" class="handle">&nbsp;</span>a</li>
    					<li id="item9"><span id="handle9" class="handle">&nbsp;</span>b</li>
    					<li id="item10"><span id="handle10" class="handle">&nbsp;</span>c</li>
    					<li id="item11"><span id="handle11" class="handle">&nbsp;</span>d</li>
    					<li id="item12"><span id="handle12" class="handle">&nbsp;</span>e</li>
    				</ul>			
    			</li>
    		</ul>
    	</li>
    	<li id="item13"><span id="handle13" class="handle">&nbsp;</span>Three</li>
    	<li id="item14"><span id="handle14" class="handle">&nbsp;</span>Four
    		<ul class="boxy sortable" id="list4">
    			<li id="item15"><span id="handle15" class="handle">&nbsp;</span>A</li>
    			<li id="item16"><span id="handle16" class="handle">&nbsp;</span>B</li>
    			<li id="item17"><span id="handle17" class="handle">&nbsp;</span>C</li>
    			<li id="item18"><span id="handle18" class="handle">&nbsp;</span>D</li>
    			<li id="item19"><span id="handle19" class="handle">&nbsp;</span>E</li>
    		</ul>
    	</li>
    </ul>
    <ul id="list5" class="boxy sortable">
    	<li id="item20"><span id="handle20" class="handle">&nbsp;</span>Five</li>
    	<li id="item21"><span id="handle21" class="handle">&nbsp;</span>Six</li>
    	<li id="item22"><span id="handle22" class="handle">&nbsp;</span>Seven</li>
    	<li id="item23"><span id="handle23" class="handle">&nbsp;</span>Eight</li>
    </ul>
    <script type="text/javascript">
    	for (var i = 1; i < 24; i++) {
    		Drag.init(document.getElementById("handle" + i), document.getElementById("item" + i));
    	}
    </script>
    </body>
    </html>
    sortable.js :

    Code:
    function findPosition(el, ex, ey) {
    	var listItems = document.getElementsByTagName("li");
    	var lx, ly, lw, lh;
    	for (var i = listItems.length-1; i >= 0; i--) {
    		if (/item/.test(listItems[i].id)) {
    			lx = xPageX(listItems[i]);
    			ly = xPageY(listItems[i]);
    			lh = xHeight(listItems[i]);
    			lw = xWidth(listItems[i])/2;
    			if ((ex >= (lx + lw)) && (ey >= ly) && (ex <= (lx + 2*lw)) && (ey <= (ly + lh))) {
    				el = el.parentNode.removeChild(el);
    				if (/\<li/.test(listItems[i].innerHTML)) {
    					var listChildren = listItems[i].getElementsByTagName("li");
    					listChildren[0].parentNode.insertBefore(el, listChildren[0]);
    				} else {
    					var ul = document.createElement("ul");
    					ul.style.className = "boxy sortable";
    					ul.style.width = "150px";
    					ul.style.listStyle = "none";
    					ul.appendChild(el);
    					listItems[i].appendChild(ul);
    				}
    				return true;
    			}
    			if (((ex >= lx) && (ey >= ly)) && ((ex <= (lx+lw)) && (ey <= (ly + lh)))) {
    				var parent = el.parentNode;
    				el = parent.removeChild(el); 
    				try {
    					listItems[i].parentNode.insertBefore(el, listItems[i]);
    				}
    				catch (e) {
    					parent.appendChild(el);
    				}
    				return true;
    			}
    		}
    	}
    	return false;
    }
    
    function replaceObj(el) {
    	var holder = el.cloneNode(false);
    	holder.style.backgroundColor = "white";
    	holder.style.border = "1px dashed black";
    	holder.innerHTML = "&nbsp;";
    	holder.id = "placeHolder";
    	el.parentNode.insertBefore(holder, el.nextSibling);
    	return holder;
    }
    
    // xPageX, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xPageX(e)
    {
      if (!(e=xGetElementById(e))) return 0;
      var x = 0;
      while (e) {
        if (xDef(e.offsetLeft)) x += e.offsetLeft;
        e = xDef(e.offsetParent) ? e.offsetParent : null;
      }
      return x;
    }
    
    // xPageY, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xPageY(el)
    {
      if (!(el=xGetElementById(el))) return 0;
      var y = 0;
      while (el) {
        if (xDef(el.offsetTop)) y += el.offsetTop;
        el = xDef(el.offsetParent) ? el.offsetParent : null;
      }
      return y;
    }
    
    // xGetElementById, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xGetElementById(el)
    {
      if(typeof(el)=='string') {
        if(document.getElementById) e=document.getElementById(el);
        else if(document.all) el=document.all[el];
        else el=null;
      }
      return el;
    }
    
    // xDef, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xDef()
    {
      for(var i=0; i<arguments.length; ++i){if(typeof(arguments[i])=='undefined') return false;}
      return true;
    }
    
    // xHeight, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xHeight(e,h)
    {
      if(!(e=xGetElementById(e))) return 0;
      if (xNum(h)) {
        if (h<0) h = 0;
        else h=Math.round(h);
      }
      else h=-1;
      var css=xDef(e.style);
      if (e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
        h = xClientHeight();
      }
      else if(css && xDef(e.offsetHeight) && xStr(e.style.height)) {
        if(h>=0) {
          var pt=0,pb=0,bt=0,bb=0;
          if (document.compatMode=='CSS1Compat') {
            var gcs = xGetComputedStyle;
            pt=gcs(e,'padding-top',1);
            if (pt !== null) {
              pb=gcs(e,'padding-bottom',1);
              bt=gcs(e,'border-top-width',1);
              bb=gcs(e,'border-bottom-width',1);
            }
            // Should we try this as a last resort?
            // At this point getComputedStyle and currentStyle do not exist.
            else if(xDef(e.offsetHeight,e.style.height)){
              e.style.height=h+'px';
              pt=e.offsetHeight-h;
            }
          }
          h-=(pt+pb+bt+bb);
          if(isNaN(h)||h<0) return;
          else e.style.height=h+'px';
        }
        h=e.offsetHeight;
      }
      else if(css && xDef(e.style.pixelHeight)) {
        if(h>=0) e.style.pixelHeight=h;
        h=e.style.pixelHeight;
      }
      return h;
    }
    
    // xNum, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xNum()
    {
      for(var i=0; i<arguments.length; ++i){if(isNaN(arguments[i]) || typeof(arguments[i])!='number') return false;}
      return true;
    }
    
    // xStr, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xStr(s)
    {
      for(var i=0; i<arguments.length; ++i){if(typeof(arguments[i])!='string') return false;}
      return true;
    }
    
    // xGetComputedStyle, Copyright 2002-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xGetComputedStyle(oEle, sProp, bInt)
    {
      var s, p = 'undefined';
      var dv = document.defaultView;
      if(dv && dv.getComputedStyle){
        s = dv.getComputedStyle(oEle,'');
        if (s) p = s.getPropertyValue(sProp);
      }
      else if(oEle.currentStyle) {
        // convert css property name to object property name for IE
        var i, c, a = sProp.split('-');
        sProp = a[0];
        for (i=1; i<a.length; ++i) {
          c = a[i].charAt(0);
          sProp += a[i].replace(c, c.toUpperCase());
        }
        p = oEle.currentStyle[sProp];
      }
      else return null;
      return bInt ? (parseInt(p) || 0) : p;
    }
    
    // xWidth, Copyright 2001-2007 Michael Foster (Cross-Browser.com)
    // Part of X, a Cross-Browser Javascript Library, Distributed under the terms of the GNU LGPL
    
    function xWidth(e,w)
    {
      if(!(e=xGetElementById(e))) return 0;
      if (xNum(w)) {
        if (w<0) w = 0;
        else w=Math.round(w);
      }
      else w=-1;
      var css=xDef(e.style);
      if (e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
        w = xClientWidth();
      }
      else if(css && xDef(e.offsetWidth) && xStr(e.style.width)) {
        if(w>=0) {
          var pl=0,pr=0,bl=0,br=0;
          if (document.compatMode=='CSS1Compat') {
            var gcs = xGetComputedStyle;
            pl=gcs(e,'padding-left',1);
            if (pl !== null) {
              pr=gcs(e,'padding-right',1);
              bl=gcs(e,'border-left-width',1);
              br=gcs(e,'border-right-width',1);
            }
            // Should we try this as a last resort?
            // At this point getComputedStyle and currentStyle do not exist.
            else if(xDef(e.offsetWidth,e.style.width)){
              e.style.width=w+'px';
              pl=e.offsetWidth-w;
            }
          }
          w-=(pl+pr+bl+br);
          if(isNaN(w)||w<0) return;
          else e.style.width=w+'px';
        }
        w=e.offsetWidth;
      }
      else if(css && xDef(e.style.pixelWidth)) {
        if(w>=0) e.style.pixelWidth=w;
        w=e.style.pixelWidth;
      }
      return w;
    }
    dom-drag.js

    Code:
    /**************************************************
     * dom-drag.js
     * 09.25.2001
     * www.youngpup.net
     **************************************************
     * 10.28.2001 - fixed minor bug where events
     * sometimes fired off the handle, not the root.
     **************************************************/
    
    var Drag = {
    
        obj : null,
    
        init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
        {
            o.onmousedown    = Drag.start;
    
            o.hmode            = bSwapHorzRef ? false : true ;
            o.vmode            = bSwapVertRef ? false : true ;
    
            o.root = oRoot && oRoot != null ? oRoot : o ;
    
            if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
            if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
            if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
            if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
    
            o.minX    = typeof minX != 'undefined' ? minX : null;
            o.minY    = typeof minY != 'undefined' ? minY : null;
            o.maxX    = typeof maxX != 'undefined' ? maxX : null;
            o.maxY    = typeof maxY != 'undefined' ? maxY : null;
    
            o.xMapper = fXMapper ? fXMapper : null;
            o.yMapper = fYMapper ? fYMapper : null;
    
            o.root.onDragStart    = new Function();
            o.root.onDragEnd    = new Function();
            o.root.onDrag        = new Function();
        },
    
        start : function(e)
        {
            var o = Drag.obj = this;
            e = Drag.fixE(e);
            var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
            var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
    		
    		o.root.onDragStart(x, y);
    
            o.lastMouseX    = e.clientX;
            o.lastMouseY    = e.clientY;
    
            if (o.hmode) {
                if (o.minX != null)    o.minMouseX    = e.clientX - x + o.minX;
                if (o.maxX != null)    o.maxMouseX    = o.minMouseX + o.maxX - o.minX;
            } else {
                if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
                if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
            }
    
            if (o.vmode) {
                if (o.minY != null)    o.minMouseY    = e.clientY - y + o.minY;
                if (o.maxY != null)    o.maxMouseY    = o.minMouseY + o.maxY - o.minY;
            } else {
                if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
                if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
            }
    
            document.onmousemove    = Drag.drag;
            document.onmouseup      = Drag.end;
    
            return false;
        },
    
        drag : function(e)
        {
            e = Drag.fixE(e);
            var o = Drag.obj;
    
            var ey    = e.clientY;
            var ex    = e.clientX;
            var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
            var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
            var nx, ny;
    
            if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
            if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
            if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
            if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
    
            nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
            ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
    
            if (o.xMapper)    	  nx = o.xMapper(y);
            else if (o.yMapper)   ny = o.yMapper(x);
    
            //Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
            //Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
            Drag.obj.lastMouseX    = ex;
            Drag.obj.lastMouseY    = ey;
    
            Drag.obj.root.onDrag(nx, ny);
    		
    		findPosition(Drag.obj.root, ex, ey);
    		
            return false;
        },
    
        end : function()
        {
            document.onmousemove = null;
            document.onmouseup   = null;
    		
            Drag.obj.root.onDragEnd(    parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), 
                                        	parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
            Drag.obj = null;
    
        },
    
        fixE : function(e)
        {
            if (typeof e == 'undefined') e = window.event;
            if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
            if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
            return e;
        }
    };
    Thanks to Aaron Boodman (at http://www.youngpup.net) for providing the drag functionality and to Mike Foster (at http://www.cross-browser.com/)
    for his numerous x helper functions.

    If anyone can spot where the problem would like in IE, I would be very grateful.

  2. #2
    SitePoint Addict Mirek Komárek's Avatar
    Join Date
    Dec 2006
    Location
    Prague
    Posts
    210
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Javascript is working, try to play with css, that position relative seems to be problem. I made small bookmarklet tool for testing css in IE, it is here IE Edit CSS. Add that green link to Favorites and then use it on your page and add this css in generated textarea. (It is beta, it works onkeyup in textarea, so when you test it there on my site, just press link, put cursor in textaerea, press space and loaded style will be editable, added to the site and interact with site, it works just with styles in link rel tag, but you can add css on any site online for testing)


    Code:
    ul.boxy,ul.boxy li{color:#000 !important;border:1px solid red !important;position:static !important}

  3. #3
    SitePoint Zealot
    Join Date
    Jul 2006
    Posts
    151
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Thanks!

    Hey thanks!

    It works beautifully now. I also added a garbage collection routine to trim the empty UL that build up.

    If anyone is willing to take a look at the script and offer improvements, that would be cool.

    Dave

  4. #4
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think the "float" on the "handle" class is causing a problem. I'm still looking into it when I can.

    // edit

    maybe not - I think Mirek was right about the 'relative'

    That's interesting - Nice work!

  5. #5
    SitePoint Member
    Join Date
    Mar 2006
    Posts
    5
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Don't know if this helps at all as you've obviously come up with a solution but I've recently put together something very similar using mootools as a base.

    It'll allow sorting in nested lists, plus a few cool things like optional sorting restrictions where you can lock sorting to a depth or only with the same parent and I've only just added optional collapsible items. Should help with sorting huge lists.

    http://www.clanccc.co.uk/moo/sortlist.html


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •