Introduction
With people pushing towards user centric development, the drag and drop feature is just one of the many such features that is commonly seen across the web pages that stress on user-centric development.This article discusses an approach to achieve such a feature keeping in mind the issues of browser compatibility.
Using the code
I have used an OOP approach for this( it surely can be done without the OOP). The main class that is responsible for drag and drop is Drag. All we need to get it going is call the Init() function. What this simply does is determine the kind of browser and set the corresponding variables.
With drag and drop what comes to my mind is
- Mouse button goes down,
- Mouse moves,
- Mouse button goes up.
We will control the location of the 'element' by retrieving the current (x,y) co-ordinates of the mouse and setting the left,top attributes of the element.
Class declaration:
function Drag()
{
this.id=""
this.beginDrag=""
this.browserType=""
this.eventObj=""
this.bodyID=""
Drag.prototype.SetBrowserType=SetBrowserType
Drag.prototype.Init=InitDragClass
Drag.prototype.GetBrowserType=GetBrowserType
Drag.prototype.BeginDrag=BeginDrag
Drag.prototype.GetDragState=GetDragState
Drag.prototype.EndDrag=EndDrag
Drag.prototype.GetCurrentCoOrdinates=GetCurrentCoOrdinates
Drag.prototype.Move=Move
Drag.prototype.SetId=SetId
Drag.prototype.GetId=GetId
Drag.prototype.SetEventObj=SetEventObj
Drag.prototype.GetEventObj=GetEventObj
Drag.prototype.RemoveChild=RemoveChild
}
function Position()
{
this.x=""
this.y=""
}
Do not be intimidated by the number of the functions,most of them are just getters and setters and are self explanatory.Here are some of which deserve an explanation:
this.id
-> This will hold the id of the 'element' that we would be dragging. this.eventObj
-> Considering the cross browser discrepencies, I have used 'eventObj' to denote the 'event' object of different browsers. this.beginDrag
-> This is a flag that is used to indicate the status of the drag operation.
function Move(obj,pos)
{
obj.style.position="absolute"
obj.style.zIndex="999"
obj.style.left=pos.x+"px"
obj.style.top=pos.y+"px"
}
function GetCurrentCoOrdinates()
{
var pos=new Position()
if(this.GetBrowserType()=="MOZILLA")
{
pos.x=(typeof this.GetEventObj().pageX =="undefined") ? 0 : this.GetEventObj().pageX
pos.y=(typeof this.GetEventObj().pageY =="undefined") ? 0 : this.GetEventObj().pageY
}
else if(this.GetBrowserType()=="IE")
{
pos.x=(this.GetEventObj().x < 0 || typeof this.GetEventObj().x=="undefined")? 0 : this.GetEventObj().x
pos.y=(this.GetEventObj().y < 0 || typeof this.GetEventObj().y=="undefined")? 0 : this.GetEventObj().y
}
return pos
}
function RemoveChild()
{
try
{
var obj=document.getElementById(this.GetId())
var objParent=obj.offsetParent
var objBody=document.getElementById(this.bodyID)
objParent.removeChild(obj)
objBody.appendChild(obj)
}
catch(err)
{
}
}
As show in the class declaration, Move() is responsible for actually moving the element in question. GetCurrentCoOrdinates() as the name suggests, returns the Position object. I will not be explaining the implementation of the rest of the functions as the name suggests their functionality and they are included in the sample source code.
RemoveChild is a bug fix. Prior to this, the elements were bounded by the area of their parent(they were unable to go left beyond their parent's area). To circumvent this problem, I remove the target element from its parent and append it to the body of the page. The body has the full page area. The old restrictions of being bounded still exist, but since the body is the page, we do not experience this bound. And since we are appending it to the body, we need to give the body an id. I set the id when calling Init().
We need to handle the onmousedown, onmousemove, onmouseup events, we need to specify explicitly that we are interested in doing so.
document.onmousemove=function(e)
{
HandleMouseMove(e)
}
document.onmouseup=function(e)
{
if(dragObj.GetId()!=null && dragObj.beginDrag==true)
{
HandleMouseUp(dragObj.GetId())
}
}
Note: There seems to be a bug in Internet Explorer because it doesn't call the 'onmouseup' event when specified in the declaration of the 'element' so I had to write it down explicitly. To handle mouse down there I have used a function HandleMouseDown(id).
function HandleMouseUp(id)
{
dragObj.EndDrag()
dragObj.SetId("")
}
function HandleMouseDown(id)
{
dragObj.BeginDrag()
dragObj.SetId(id)
}
function HandleMouseMove(eventObj)
{
if(typeof eventObj!="undefined")
{
if(dragObj.GetId()!=null && dragObj.beginDrag==true)
{
dragObj.SetEventObj(eventObj)
dragObj.RemoveChild()
dragObj.Move(document.getElementById(dragObj.GetId()),dragObj.GetCurrentCoOrdinates())
}
}
else
{
if(dragObj.GetId()!=null && dragObj.beginDrag==true)
{
dragObj.SetEventObj(event)
dragObj.RemoveChild()
dragObj.Move(document.getElementById(dragObj.GetId()),dragObj.GetCurrentCoOrdinates())
}
}
}
When the drag ends the zIndex would be set back to null.Setting zIndex brings the element in question in the foreground. Finally you would have to add this piece of code while declaring the element :
onmousedown="javascript:HandleMouseDown(this.id)" onmouseup="javascript:HandleMouseUp(this.id)" onmousemove="HandleMouseMove(this.id)"
We are now ready to drag. I have attached a sample for reference.
History
v 1.0 Added
v 1.1 A bug fix,elements now aren't bounded by their parent's area.