[Last Call] Learn about multicloud storage options and how to improve your company's cloud strategy. Register Now


Drag and Drop between 2 list boxes in Javascript

Posted on 2004-10-21
Medium Priority
Last Modified: 2008-01-09
I want to have the ability to drag and drop between 2 list boxes. If one selection is dragged to other list box, I want the original selection removed.  Conversely, I also want to drag in the opposite direction. So if I have a selection in the second list box and I drag it back to the fist, I want it to be removed from the second list box.  Optimally, if I can have it sort alphabetically by the selections, that would be great.  I need this to be compatible with IE and NS.  

Hope this is clear - thanks for the help.

Question by:DrData68
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 8
  • 4
  • 3
  • +1
LVL 13

Expert Comment

ID: 12376170
Don't know about drag and drop, but this works just as well. Probably nicer:


Author Comment

ID: 12376510
I am actually a fan of Matt- I used some of his Javascript before, but I really wanted to get the drag and drop thing going as well as the buttons.  

LVL 13

Expert Comment

ID: 12376531
Consider why you want drag-and-drop. Is it because it's "cool," or is there a valid user-interface reason for it?
My suggestion is to just keep it simple. What are you trying to accomplish with drag-and-drop, exactly?
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!


Author Comment

ID: 12376823
I am thinking that I might try and get some sorting going in the list boxes, so it might be name, state, group. So if they want to pull 10 to 20 names from a particular state over, I do not want them to have to click 20 times.  If they resort, I only want to see the remaining selections, and if they want to pull another 20 or 30 people over that belong to a certain group - again, I do not want them to click 20 times.

Make sense?

Expert Comment

ID: 12378653
Since this was an interesting puzzle, I spend a few hours on it - not just to answer your question, but also as a form of geek-entertainement. I have to report that this is probably not going to work as straight-forward as you may have expected it.

First, I got it working fine in Mozilla-based browsers for single list elements, and it's actually pretty cool to click on a list element and be able to drag it to the other list - I even painted the list element itself next to the mouse pointer.

Then the problems started. When testing the finished code in IE, it turned out that the build-in event handler that actually changes the selections of the list gets executed AFTER the event is passed to my custom event handler. So when a user holds down the mouse button on an element in that list, that list's selectedIndex property is not updated when I read it. I tried some hackery with delayed evaluation of the list's selected items, but I would have to delay over 500 ms for that to work, and that makes the whole thing unusable from an interface-experience-perspective. Moreover, the <option> tag in IE does NOT support the onMouseDown event, which was what made it work in Firefox/Mozilla. There may be a possibility with some other hackery to get it working for a single element, but that was not what you wanted anyway.

You wanted drag and drop on multiple elements. Although I was able to write the logic for that to happen in Mozilla & Co., the user will never be able to do it and here is why: When you give a list field the ability to have multiple options selected, you can perform this selection by holding the mouse down and moving it accross the items you want to select. Unfortunately, that is the same process you would use to get it dragged. So everytime you try to grab on to a selection, instead of you being able to drag it, the browser will just start a new selection. I was not able to simply cancel the event after I handled it in Mozilla, and because of IE being screwed up with the simple stuff, I did try there either.

Although the idea is great, its implementation may prove more difficult. There is an entirely different approach that would be much easier to implement and could be made to work cross-browser fairly easily. Instead of using a real form field list element, a scrollable list could be simulated with a <div> containing <span>s or <p>s and having its overflow property set to auto, making sure none of the contents in the div can be wider than the div itself. The simulation of the list-behavior would be handled entirely in JavaScript, updating hidden form fields with the actual internal values for which items are in which list. Let me know whether you think this approach would be worth your 500 points and I give it a try.

Accepted Solution

cwolves earned 1500 total points
ID: 12381593
something like this?  (sigmacon, this is basically what you suggested, I've already done it though  :-))

the javascript isn't perfect as I havn't had time to finish it, but it should work for this purpose I think

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
      width:            100%;
      background-color: #E6E6E6;
      color:            #000000;
      font-family:      tahoma;
      font-size:        10px;
      font-weight:      bold;
      padding:          2px;
      cursor:           hand;
      position:         relative;
      padding:          1px;
      overflow:         hidden;
      position:         relative;
      position:         absolute;

<div id="Nav0" style="width:100px;float:left;border:1px solid #000000;" h="false"></div>
<div id="Nav1" style="width:100px;float:left;border:1px solid #000000;" h="false"></div>

<div id="Holder" class="DragH" style="display:none;filter:alpha(opacity=50);"></div>

<div id="WindowDefs">
      <div id="Window0" style="display:none;" class="Drag"><div class="Title">Left Option #1</div></div>
      <div id="Window1" style="display:none;" class="Drag"><div class="Title">Left Option #2</div></div>
      <div id="Window2" style="display:none;" class="Drag"><div class="Title">Left Option #3</div></div>
      <div id="Window3" style="display:none;" class="Drag"><div class="Title">Right Option #1</div></div>
      <div id="Window4" style="display:none;" class="Drag"><div class="Title">Right Option #2</div></div>
      <div id="Window5" style="display:none;" class="Drag"><div class="Title">Right Option #3</div></div>
var Containers = 2;
var ConPrefix  = 'Nav';
var ModPrefix  = 'Window';
var Modules    = 6;
var DropColor  = '#FF0000';
var Locations  = new Array(new Array(0, 1, 2), new Array(3, 4, 5));

var Drag       = false;
var e          = null;
var h          = document.getElementById('Holder');
var x, y, t, pOpt, optDrop, D, oBorder;

function addElement(array, addindex, addvalue) {
      size    = array.length;
      validNo = (addindex != "NaN");
      inRange = ( (addindex >= 0) && (addindex <= array.length+1) );

      if (validNo && inRange) {
            for (var i=array.length+1; i>addindex; i--) array[i] = array[i-1];
                  array[addindex] = addvalue;
            array.length = array.length-1;
      else alert("You only add element indexes between 0 and " + (size) + ".");

function deleteElement(array, delindex) {
      size    = array.length;
      validNo = (delindex != "NaN");
      inRange = ( (delindex >= 0) && (delindex <= array.length) );

      if (validNo && inRange && size>0) {
            for(var i=delindex; i<size-1; i++){
                  array[i] = array[i+1];
      else alert("You only delete from element index 0 to " + (size-1) + ".");

function ReDrawWindows(){
      var Content = new Array();
      for(var i=0; i<Containers; i++){
            Content[i] = '';
            for(var j=0; j<Locations[i].length; j++)
                  Content[i]+=document.getElementById(ModPrefix+Locations[i][j]).outerHTML.replace('DISPLAY: none;', '').replace('DISPLAY: none', '').replace(ModPrefix+Locations[i][j], ConPrefix+ModPrefix+'_'+i+'_'+j);
            for(var j=0; j<Locations[i].length; j++)
//                  alert((100/Locations[i].length)+'%');

function MouseDown(){
      e    = event.srcElement;
      for(var i=0; i<30 && e.className!='Drag'; i++)
            try{if(e.parentElement) e = e.parentElement;}catch(err){}
            x    = event.clientX-e.offsetLeft;
            y    = event.clientY-e.offsetTop;
            h.innerHTML        = e.innerHTML;
            h.style.width      = e.offsetWidth;
            h.style.height     = e.offsetHeight;
            h.style.display    = '';
            h.style.pixelTop   = e.offsetTop;
            h.style.pixelLeft  = e.offsetLeft;
            Drag               = true;
            e.style.visibility = 'hidden';

function MouseUp(){
      Drag               = false;
      h.style.display    = 'none';
      if(!e) return false;
      if(typeof(optDrop)!='undefined'){      // we have a valid drop!
//            document.getElementById(ModPrefix+Locations[parseInt(loc[1])][parseInt(loc[2])]).innerHTML=optDrop.innerHTML;
                  var to   = (optDrop.id.indexOf('_')>=0?optDrop.id:ConPrefix+ModPrefix+'_'+optDrop.id.substr(ConPrefix.length)+'_0').split('_');
                  var from = e.id.split('_');
                  var oSrc = Locations[from[1]][from[2]];
                  deleteElement(Locations[from[1]], parseInt(from[2]));
                  addElement(Locations[to[1]], parseInt(to[2])+parseInt(D=='Y1'?1:0)-(to[1]==from[1] && to[2]>from[2]?1:0), oSrc);
            } catch(err) {}
      if(typeof(pOpt)!='undefined') pOpt.style.border=oBorder;
      if(e.className=='Drag') ReDrawWindows();
      e       = null;
      optDrop = null;

function MouseMove(){
      if(!Drag || e==null) return false;
      if(typeof(pOpt)!='undefined') pOpt.style.border=oBorder;
      h.style.pixelTop  = event.clientY-y;
      h.style.pixelLeft = event.clientX-x;

      // find the best spot to drop, if applicable
      var c, o, d, mD=5000, tD;
      for(var i=0; i<Containers; i++){
            c = document.getElementById(ConPrefix+i);

            // check the children of each container
            for(var j=0; j<c.children.length; j++){
                  o  = c.children[j];
                  d  = getDistance(o);
                  tD = checkOpt(o, c.h=='false'?false:true, c.v=='false'?false:true);
                  if(tD.mD<mD) { mD = tD.mD; D = tD.D; optDrop = o; }

            // check the container itself (in case it has no children)
                  d  = getDistance(c);
                  tD = checkOpt(c, c.h=='false'?false:true, c.v=='false'?false:true);
                  if(tD.mD<mD) { mD = tD.mD; D = tD.D; optDrop = c; }
      if (typeof(optDrop)!='undefined'){
            oBorder = optDrop.style.border;
                  optDrop.style.borderTop    = '1px solid #FF0000';
            }else if(D=='Y1'){
                  optDrop.style.borderBottom = '1px solid #FF0000';
            }else if(D=='X0'){
                  optDrop.style.borderLeft   = '1px solid #FF0000';
            }else if(D=='X1'){
                  optDrop.style.borderRight  = '1px solid #FF0000';
      pOpt = optDrop;
      return false;

function getDistance(o){
      return {x:h.offsetLeft-o.offsetLeft, y:h.offsetTop-o.offsetTop};

function checkOpt(o, horiz, vert){
      var mD=5000, d, D;

            // check top
            if(h.offsetTop<=o.offsetTop && (h.offsetTop+h.offsetHeight)>=o.offsetTop && h.offsetLeft<o.offsetLeft+o.offsetWidth && h.offsetLeft+h.offsetWidth>o.offsetLeft) {
                  d = Math.pow(Math.pow((h.offsetTop+h.offsetHeight)-(o.offsetTop), 2)+Math.pow((h.offsetLeft+h.offsetWidth/2)-(o.offsetLeft+o.offsetWidth/2), 2), .5)
                  if(d<mD){ mD = d; D='Y0'; }
            // check bottom
            if(h.offsetTop<=(o.offsetTop+o.offsetHeight) && (h.offsetTop+h.offsetHeight)>=(o.offsetTop+o.offsetHeight) && h.offsetLeft<o.offsetLeft+o.offsetWidth && h.offsetLeft+h.offsetWidth>o.offsetLeft) {
                  d = Math.pow(Math.pow((h.offsetTop)-(o.offsetTop+o.offsetHeight), 2)+Math.pow((h.offsetLeft+h.offsetWidth/2)-(o.offsetLeft+o.offsetWidth/2), 2), .5)
                  if(d<mD){ mD = d; D='Y1'; }
      // check left
            if(h.offsetLeft<=o.offsetLeft && (h.offsetLeft+h.offsetWidth)>=o.offsetLeft && h.offsetTop<o.offsetTop+o.offsetHeight && h.offsetTop+h.offsetHeight>o.offsetTop) {
                  d = Math.pow(Math.pow((h.offsetLeft+h.offsetWidth)-(o.offsetLeft), 2)+Math.pow((h.offsetTop+h.offsetHeight/2)-(o.offsetTop+o.offsetHeight/2), 2), .5)
                  if(d<mD){ mD = d; D='X0'; }
            // check right
            if(h.offsetLeft<=(o.offsetLeft+o.offsetWidth) && (h.offsetLeft+h.offsetWidth)>=(o.offsetLeft+o.offsetWidth) && h.offsetTop<o.offsetTop+o.offsetHeight && h.offsetTop+h.offsetHeight>o.offsetTop) {
                  d = Math.pow(Math.pow((h.offsetLeft)-(o.offsetLeft+o.offsetWidth), 2)+Math.pow((h.offsetTop+h.offsetHeight/2)-(o.offsetTop+o.offsetHeight/2), 2), .5)
                  if(d<mD){ mD = d; D='X1'; }
      return {mD:mD,D:D};

function select(){
      return false;

function getHTML(url) {
      var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      xmlhttp.open("GET", url + "&randomnum=" + Math.floor(Math.random()*1000000), false);
      return xmlhttp.responseText;

document.onmousedown = MouseDown;
document.onmousemove = MouseMove;
document.onmouseup   = MouseUp;
document.onselect    = select;


Author Comment

ID: 12381708
cwolves - this is nice.  Is there any way I can get multiple selections going?  I am anticipating a pretty large list,
and if they have the ability to do a multiple select of 20 or 30 at a time and drag it - that would be great!!


Expert Comment

ID: 12381923
not easily.  how well do you kow JS?

Author Comment

ID: 12381961
I would classify myself as high intermediate to lower advanced.
What do you suggest?

Expert Comment

ID: 12382154
basically, 'e' in that code is the selected div tag.  in the mousedown function, you can check the event.ctrlKey property and if it's true, make 'e' an array and add event.srcElement to it.

'h' is the "ghost" element (it's the semi-transparent div tag that actually moves around when you drag the mouse).  Set that to e[0].

And on the mouseup tag, loop through each element in e instead of just referencing e in the try{} statement.

I'd do it myself but I have -no- time today, especially not to debug it  :-/

Author Comment

ID: 12382181
Let me take a look at it and see what I can do.

Expert Comment

ID: 12403790
DrData, we wouldn't mind seeing the finished product ;-)

Author Comment

ID: 12403964
I agree.  I got pulled onto other things, so I have not done too much with it.

Expert Comment

ID: 12470431
DrData68, do you need more help regarding this question?

Author Comment

ID: 12472963
Yes, that would be great!!!

Expert Comment

ID: 12476048
Well, any specific questions? As I pointed out, doing what you where trying to do with actual <select>s is not going to work. Did you try cwolves' approach?

Author Comment

ID: 12476870
I tried playing with the HTML first and started going in the wrong direction.  I just reread his message and understand that I have to add code to the mouse down event.  Looking at it, i am not sure how to handle the code for the determining the className equal to 'Drag' if I have multiple selections and e is an array.  Any ideas?

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A while back, I ran into a situation where I was trying to use the calculated columns feature in SharePoint 2013 to do some simple math using values in two lists. Between certain data types not being accessible, and also with trying to make a one to…
In this blog, we’ll look at how improvements to Percona XtraDB Cluster improved IST performance.
The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…

650 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question