Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 355
  • Last Modified:

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?
0
ag73
Asked:
ag73
1 Solution
 
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
 
ag73Author Commented:
Forgot to mention that the layer that pops up must have the ability to be repositioned to another drop target.
0
Technology Partners: 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!

 
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
 
lil_puffballCommented:
I don't think they both popped out, it's just that the bottom layer got hidden under the target... x_x
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

Featured Post

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!

Tackle projects and never again get stuck behind a technical roadblock.
Join Now