Solved

Draggable layers with several drop targets

Posted on 2004-04-22
16
344 Views
Last Modified: 2006-11-17
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
Comment
Question by:ag73
16 Comments
 
LVL 12

Expert Comment

by:lil_puffball
ID: 10905281
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
 

Author Comment

by:ag73
ID: 10915646
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
 

Author Comment

by:ag73
ID: 10915664
Forgot to mention that the layer that pops up must have the ability to be repositioned to another drop target.
0
 
LVL 12

Expert Comment

by:lil_puffball
ID: 10924073
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
 

Author Comment

by:ag73
ID: 10925494
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
 
LVL 12

Expert Comment

by:lil_puffball
ID: 10932583
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
 

Author Comment

by:ag73
ID: 10946187
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
 
LVL 12

Expert Comment

by:lil_puffball
ID: 10953308
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
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 12

Accepted Solution

by:
lil_puffball earned 500 total points
ID: 10953315
I don't think they both popped out, it's just that the bottom layer got hidden under the target... x_x
0
 

Author Comment

by:ag73
ID: 10972039
Great work! Thank you very much for beeing so helpful.
regards | andré
0
 
LVL 12

Expert Comment

by:lil_puffball
ID: 10972055
Glad to help. :) Thanks for the points and the A.
0
 

Expert Comment

by:msacks_us
ID: 11108888
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
 
LVL 12

Expert Comment

by:lil_puffball
ID: 11111519
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
 

Author Comment

by:ag73
ID: 11124733
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
 

Expert Comment

by:R208
ID: 11178667
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
 
LVL 12

Expert Comment

by:lil_puffball
ID: 11326868
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

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Suggested Solutions

In my daily work (mainly using ASP.net), I need to write a lot of JavaScript code. One of the most repetitive tasks I do are the jQuery Ajax calls. You know: (CODE) I don't know if for you it's the same, but for me is soooo tedious to write the …
Nothing in an HTTP request can be trusted, including HTTP headers and form data.  A form token is a tool that can be used to guard against request forgeries (CSRF).  This article shows an improved approach to form tokens, making it more difficult to…
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…

747 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now