Draggable layers with several drop targets

Hi I have a delicate little problem!
I think this would be a good idea for generating a schedule.
I would like to generate x amount of layers (created dynamically with xslt) that are draggable. I would also like to generate x amount of drop targets for these layers. The user should be able to place any layer on any drop target (drop targets should also have a snap to area of 10pixels). This way you can have for example a weekly schedule with x amount of timeslots. You also have the classes (maybe; math, english, history, and so on) that you would like to fill the timeslots with. The classes can be distributed in to the time slots arbitrarily when the page is generated but then the user should be able to move the classes around to suit his or her needs. When the user is satisfied with the schedule then he or she can press a submit button and the position of the layers are registered and handed to a servlet which sends the appropriate info along to a database. Can anyone help me with the javascripting part of this?
ag73Asked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
lil_puffballConnect With a Mentor Commented:
I don't think they both popped out, it's just that the bottom layer got hidden under the target... x_x
0
 
lil_puffballCommented:
Something like this maybe?

<script>
var numLayers=0,startPos=100,range=10;
var drag=false,obj=null,offL,offT,oldL,oldT;

if(document.layers){
document.captureEvents(Event.MOUSEMOVE);}
document.onmousemove=captureMouse;

function startDrag(srcEl){
  //set the object
  obj=srcEl;
  oldL=getLeft(obj);
  oldT=getTop(obj);
  //calculate position of mouse compared to position of object
  offL=x-oldL;
  offT=y-oldT;
  //set drag to true
  drag=true;
}
function dragObj(){
  //change position of object
  obj.style.left=x-offL;
  obj.style.top=y-offT;
}
function endDrag(){
  //set drag to false
  drag=false;
  //snap
  snapToTarget();
  obj=null;
}
function getLeft(obj){return parseInt(obj.style.left);}
function getTop(obj){return parseInt(obj.style.top);}
function captureMouse(e){
  if(document.all){
    x=window.event.x+document.body.scrollLeft;
    y=window.event.y+document.body.scrollTop;
  }else if(document.getElementById||document.layers){
    x=e.pageX;
    y=e.pageY;
  }
  if(drag){
dragObj();}
}

function snapToTarget(){
  var target;
  for(i=1;i<=numLayers;i++){
    target=document.all?document.all["target"+i]:document.getElementById("target"+i);
    if(Math.abs(getLeft(obj)-getLeft(target))<=range&&Math.abs(getTop(obj)-getTop(target))<=range){
      obj.style.left=target.style.left;
      obj.style.top=target.style.top;
      return;
    }
  }
  alert('Please drag to a target.');
  obj.style.left=oldL;obj.style.top=oldT;
}

function addLayer(){
  numLayers++;
  var layer=document.createElement("div");
  layer.style.position="absolute";
  layer.style.width=100;
  layer.style.height=30;
  layer.unselectable="on";
  layer.style.backgroundColor="99ccff";
  layer.style.left=100;
  layer.style.top=numLayers*40+startPos;
  var target=layer.cloneNode(true);
  target.style.backgroundColor="000099";
  target.style.left=250;
  target.id="target"+numLayers;
  target.appendChild(document.createTextNode("Target"));
  target.style.zIndex=0;
  document.body.appendChild(target);
  layer.onmousedown=function(){startDrag(this);}
  layer.appendChild(document.createTextNode("Layer"));
  layer.style.zIndex=1;
  document.body.appendChild(layer);
}
</script>

<body onmouseup="if(obj){endDrag();}">

<button onclick="addLayer();">Add Layer</button>

0
 
ag73Author Commented:
Yes, I have only one problem with this solution, I can stack several layers on top of eachother. This is not good since the classes will be hard to find then. A solution to this would maybe be to have the layer already positioned on a drop target pop up to the top of the stacking order and move, maybe 15-20 pixels, outside of the drop target. This way there is no risk of classes getting stacked on top of eachother.
0
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

 
ag73Author Commented:
Forgot to mention that the layer that pops up must have the ability to be repositioned to another drop target.
0
 
lil_puffballCommented:
This new script conforms to your first request, however I'm not sure what second request means, doesn't it do that already?

<script>
var numLayers=0,startPos=100,range=10,offset=20;
var drag=false,obj=null,offL,offT,oldL,oldT;

var layersRecord=new Array();

if(document.layers){
document.captureEvents(Event.MOUSEMOVE);}
document.onmousemove=captureMouse;

function startDrag(srcEl){
  //set the object
  obj=srcEl;
  oldL=getLeft(obj);
  oldT=getTop(obj);
  //calculate position of mouse compared to position of object
  offL=x-oldL;
  offT=y-oldT;
  obj.style.zIndex=numLayers+1;
  //set drag to true
  drag=true;
}
function dragObj(){
  //change position of object
  obj.style.left=x-offL;
  obj.style.top=y-offT;
}
function endDrag(){
  //set drag to false
  drag=false;
  //snap
  snapToTarget();
  obj=null;
}
function getLeft(obj){return parseInt(obj.style.left);}
function getTop(obj){return parseInt(obj.style.top);}
function captureMouse(e){
  if(document.all){
    x=window.event.x+document.body.scrollLeft;
    y=window.event.y+document.body.scrollTop;
  }else if(document.getElementById||document.layers){
    x=e.pageX;
    y=e.pageY;
  }
  if(drag){
dragObj();}
}

function snapToTarget(){
  var target;
  for(var i=1;i<=numLayers;i++){
    target=document.all?document.all["target"+i]:document.getElementById("target"+i);
    if(Math.abs(getLeft(obj)-getLeft(target))<=range&&Math.abs(getTop(obj)-getTop(target))<=range){
      obj.style.left=target.style.left;
      obj.style.top=target.style.top;
      layersRecord[i][layersRecord[i].length]=obj;
      if(obj.parent.parent){removeRecord(obj);}
      obj.parent.parent=layersRecord[i];
      if(layersRecord[i].length>1){unStack(i,target);}
      return;
    }
  }
  alert('Please drag to a target.');
  obj.style.left=oldL;obj.style.top=oldT;
}

function removeRecord(obj){
  var rec=obj.parent.parent,found=false;
  for(var i=0;i<rec.length;i++){
    if(found){
      rec[i-1]=rec[i];
    }else{
      if(rec[i]==obj){found=true;}
    }
  }
  if(found){rec.length=rec.length-1;}
}

function unStack(n,target){
  for(i=0;i<layersRecord[n].length;i++){
    layersRecord[n][i].style.left=parseInt(target.style.left)+offset*i;
    layersRecord[n][i].style.top=parseInt(target.style.top)+offset*i;
    layersRecord[n][i].style.zIndex=i;
  }
}

function parent(){
this.parent=null;}

function addLayer(){
  numLayers++;
  var layer=document.createElement("div");
  layer.style.position="absolute";
  layer.style.width=100;
  layer.style.height=30;
  layer.unselectable="on";
  layer.style.backgroundColor="99ccff";
  layer.style.left=100;
  layer.style.top=numLayers*40+startPos;
  var target=layer.cloneNode(true);
  target.style.backgroundColor="000099";
  target.style.left=250;
  target.id="target"+numLayers;
  target.appendChild(document.createTextNode("Target #"+numLayers));
  target.style.zIndex=0;
  document.body.appendChild(target);
  layer.id="layer"+numLayers;
  layer.onmousedown=function(){startDrag(this);}
  layer.appendChild(document.createTextNode("Layer #"+numLayers));
  layer.style.zIndex=1;
  layer.parent=new parent;
  document.body.appendChild(layer);
  layersRecord[numLayers]=new Array();
}
</script>

<body onmouseup="if(obj){endDrag();}">

<button onclick="addLayer();">Add Layer</button>
0
 
ag73Author Commented:
Great work! However, there is still one minor problem remaining. I would like the layer that sits on a drop target to pop up and out when you are trying to position another layer there, not like it is now when the layer that you are trying to place on a drop target pops up and out.
0
 
lil_puffballCommented:
How about this:

<script>
var numLayers=0,startPos=100,range=10,offset=20;
var drag=false,obj=null,offL,offT,oldL,oldT;

var layersRecord=new Array();

if(document.layers){
document.captureEvents(Event.MOUSEMOVE);}
document.onmousemove=captureMouse;

function startDrag(srcEl){
  //set the object
  obj=srcEl;
  oldL=getLeft(obj);
  oldT=getTop(obj);
  //calculate position of mouse compared to position of object
  offL=x-oldL;
  offT=y-oldT;
  obj.style.zIndex=numLayers+1;
  //set drag to true
  drag=true;
}
function dragObj(){
  //change position of object
  obj.style.left=x-offL;
  obj.style.top=y-offT;
}
function endDrag(){
  //set drag to false
  drag=false;
  //snap
  snapToTarget();
  obj=null;
}
function getLeft(obj){return parseInt(obj.style.left);}
function getTop(obj){return parseInt(obj.style.top);}
function captureMouse(e){
  if(document.all){
    x=window.event.x+document.body.scrollLeft;
    y=window.event.y+document.body.scrollTop;
  }else if(document.getElementById||document.layers){
    x=e.pageX;
    y=e.pageY;
  }
  if(drag){
dragObj();}
}

function snapToTarget(){
  var target;
  for(var i=1;i<=numLayers;i++){
    target=document.all?document.all["target"+i]:document.getElementById("target"+i);
    if(Math.abs(getLeft(obj)-getLeft(target))<=range&&Math.abs(getTop(obj)-getTop(target))<=range){
      obj.style.left=target.style.left;
      obj.style.top=target.style.top;
      layersRecord[i][layersRecord[i].length]=obj;
      if(obj.parent.parent){removeRecord(obj);}
      obj.parent.parent=layersRecord[i];
      if(layersRecord[i].length>1){unStack(i,target);}
      return;
    }
  }
  alert('Please drag to a target.');
  obj.style.left=oldL;obj.style.top=oldT;
}

function removeRecord(obj){
  var rec=obj.parent.parent,found=false;
  for(var i=0;i<rec.length;i++){
    if(found){
      rec[i-1]=rec[i];
    }else{
      if(rec[i]==obj){found=true;}
    }
  }
  if(found){rec.length=rec.length-1;}
}

function unStack(n,target){
  for(i=layersRecord[n].length-1;i>=0;i--){
    layersRecord[n][i].style.left=parseInt(target.style.left)+offset*(layersRecord[n].length-i-1);
    layersRecord[n][i].style.top=parseInt(target.style.top)+offset*(layersRecord[n].length-i-1);
    layersRecord[n][i].style.zIndex=layersRecord[n].length-i-1;
  }
}

function parent(){
this.parent=null;}

function addLayer(){
  numLayers++;
  var layer=document.createElement("div");
  layer.style.position="absolute";
  layer.style.width=100;
  layer.style.height=30;
  layer.unselectable="on";
  layer.style.backgroundColor="99ccff";
  layer.style.left=100;
  layer.style.top=numLayers*40+startPos;
  var target=layer.cloneNode(true);
  target.style.backgroundColor="000099";
  target.style.left=250;
  target.id="target"+numLayers;
  target.appendChild(document.createTextNode("Target #"+numLayers));
  target.style.zIndex=0;
  document.body.appendChild(target);
  layer.id="layer"+numLayers;
  layer.onmousedown=function(){startDrag(this);}
  layer.appendChild(document.createTextNode("Layer #"+numLayers));
  layer.style.zIndex=1;
  layer.parent=new parent;
  document.body.appendChild(layer);
  layersRecord[numLayers]=new Array();
}
</script>

<body onmouseup="if(obj){endDrag();}">

<button onclick="addLayer();">Add Layer</button>
0
 
ag73Author Commented:
YES! almost there I believe, one minor bug to work out though. When creating two layers and droptargets I drag layer#1 to target#1 and same thing with layer#2 and everything is fine. However, when I try to drag layer#1 from target#1 to target#2 (where layer#2 is already sitting) both of the layers pop up and out and get stacked on top of eachother. Do you have a solution to this problem?
0
 
lil_puffballCommented:
Oops....This will do it:

<script>
var numLayers=0,startPos=100,range=10,offset=20;
var drag=false,obj=null,offL,offT,oldL,oldT;

var layersRecord=new Array();

if(document.layers){
document.captureEvents(Event.MOUSEMOVE);}
document.onmousemove=captureMouse;

function startDrag(srcEl){
  //set the object
  obj=srcEl;
  oldL=getLeft(obj);
  oldT=getTop(obj);
  //calculate position of mouse compared to position of object
  offL=x-oldL;
  offT=y-oldT;
  obj.style.zIndex=numLayers+1;
  //set drag to true
  drag=true;
}
function dragObj(){
  //change position of object
  obj.style.left=x-offL;
  obj.style.top=y-offT;
}
function endDrag(){
  //set drag to false
  drag=false;
  //snap
  snapToTarget();
  obj=null;
}
function getLeft(obj){return parseInt(obj.style.left);}
function getTop(obj){return parseInt(obj.style.top);}
function captureMouse(e){
  if(document.all){
    x=window.event.x+document.body.scrollLeft;
    y=window.event.y+document.body.scrollTop;
  }else if(document.getElementById||document.layers){
    x=e.pageX;
    y=e.pageY;
  }
  if(drag){
dragObj();}
}

function snapToTarget(){
  var target;
  for(var i=1;i<=numLayers;i++){
    target=document.all?document.all["target"+i]:document.getElementById("target"+i);
    if(Math.abs(getLeft(obj)-getLeft(target))<=range&&Math.abs(getTop(obj)-getTop(target))<=range){
      obj.style.left=target.style.left;
      obj.style.top=target.style.top;
      layersRecord[i][layersRecord[i].length]=obj;
      if(obj.parent.parent){removeRecord(obj);}
      obj.parent.parent=i;
      if(layersRecord[i].length>1){unStack(i,target);}
      return;
    }
  }
  alert('Please drag to a target.');
  obj.style.left=oldL;obj.style.top=oldT;
}

function removeRecord(obj){
  var rec=layersRecord[obj.parent.parent],found=false;
  for(var i=0;i<rec.length;i++){
    if(found){
      rec[i-1]=rec[i];
    }else{
      if(rec[i]==obj){found=true;}
    }
  }
  if(found){rec.length=rec.length-1;}
  i=obj.parent.parent;
  if(rec.length>0){unStack(i,document.all?document.all["target"+i]:document.getElementById("target"+i));}
}

function unStack(n,target){
  for(i=layersRecord[n].length-1;i>=0;i--){
    layersRecord[n][i].style.left=parseInt(target.style.left)+offset*(layersRecord[n].length-i-1);
    layersRecord[n][i].style.top=parseInt(target.style.top)+offset*(layersRecord[n].length-i-1);
    layersRecord[n][i].style.zIndex=layersRecord[n].length-i;
  }
}

function parent(){
this.parent=null;}

function addLayer(){
  numLayers++;
  var layer=document.createElement("div");
  layer.style.position="absolute";
  layer.style.width=100;
  layer.style.height=30;
  layer.unselectable="on";
  layer.style.backgroundColor="99ccff";
  layer.style.left=100;
  layer.style.top=numLayers*40+startPos;
  var target=layer.cloneNode(true);
  target.style.backgroundColor="000099";
  target.style.left=250;
  target.id="target"+numLayers;
  target.appendChild(document.createTextNode("Target #"+numLayers));
  target.style.zIndex=0;
  document.body.appendChild(target);
  layer.id="layer"+numLayers;
  layer.onmousedown=function(){startDrag(this);}
  layer.appendChild(document.createTextNode("Layer #"+numLayers));
  layer.style.zIndex=1;
  layer.parent=new parent;
  document.body.appendChild(layer);
  layersRecord[numLayers]=new Array();
}
</script>

<body onmouseup="if(obj){endDrag();}">

<button onclick="addLayer();">Add Layer</button>
0
 
ag73Author Commented:
Great work! Thank you very much for beeing so helpful.
regards | andré
0
 
lil_puffballCommented:
Glad to help. :) Thanks for the points and the A.
0
 
msacks_usCommented:
This is a really great script.  I created a test page based on the script above, wrapped the <button> inside a form tag, but I can't figure out how to get the values from the layers/targets when the form is submitted.  Can someone post an example of how to do that?
0
 
lil_puffballCommented:
msacks_us,
You probably should post a new question for this. I'm rather busy these days so I'm unable to help you out now. I'm sure there are other experts who can help you though. If no one replies, come back and let me know.
0
 
ag73Author Commented:
Please notify me by posting to this thread if you find a way to achieve this. It would be a great supplement for me as well.
0
 
R208Commented:
Very interresing solution.

Any hint to drag and drop between to different container.

I am trying to have a div containing an array of drop targets and a his left an scrollable div containing the draggable divs.

It's for creating image galleries.

Régis. (http://R208.com)
0
 
lil_puffballCommented:
Hi R208,

Again, I will suggest to you what I suggested to msacks_us:

"You probably should post a new question for this. I'm rather busy these days so I'm unable to help you out now. I'm sure there are other experts who can help you though. If no one replies, come back and let me know."
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.