There are many libraries like jQuery which provide plugins to make an
DOM element movable and draggable. I would like, however, to know how to make
an element draggable without external library. In [1], the author provides an
implementation of draggable element both using keyboard and mouse. He also gave
an post (see [3]) about HTML5 draggable issue. His implementation:
includes both keyboard and mouse, but I want mouse only.
this keyword in the callback function refers to the DOM element at which
events occur, but I want this keyword to refer to the object which makes
DOM element draggable.
In [2], the author also gives another implementation, but I like the code
structure to be more "object-oriented" like the implementation in [1]. So I
wrote an implementation of my own. The following is my implementation:
<!doctype html><html><head><metacharset="utf-8"><title>Draggable (Movable, Drag and Drop) DOM Element Example</title></head><body><divid="dragMe"style="width: 200px; height: 200px; background: yellow;">Drag Me</div><scripttype="text/javascript"src="draggable.js"></script><scripttype="text/javascript">window.onload=function(){vardrag=newDraggable('dragMe');}</script></body></html>
/** * @fileoverview Class to make DOM element draggable. * * References: * @see http://www.quirksmode.org/js/dragdrop.html * @see http://luke.breuer.com/tutorial/javascript-drag-and-drop-tutorial.aspx * @see http://help.dottoro.com/ljwcseaq.php * @see http://help.dottoro.com/ljlrboji.php * @see http://help.dottoro.com/ljsjcrav.php *//** * Cross-browser addEventListener function. * * @param {DOM element} element The element to add event listener. * @param {string} evt The event to be listened. * @param {function} fn The callback function when event occurs. */addEventListener=function(element,evt,fn){if(window.addEventListener){/* W3C compliant browser */element.addEventListener(evt,fn,false);}else{/* IE */element.attachEvent('on'+evt,fn);}};/** * Cross-browser removeEventListener function. * * @param {DOM element} element The element to remove event listener. * @param {string} evt The event to be un-listened. * @param {function} fn The callback function when event occurs. */removeEventListener=function(element,evt,fn){if(window.removeEventListener){/* W3C compliant browser */element.removeEventListener(evt,fn,false);}else{/* IE */element.detachEvent('on'+evt,fn);}};/** * Class to make DOM element draggable. * * @param {string} id The id of DOM element to be draggable. * @constructor */Draggable=function(id){/** * The DOM element to be made draggable. * @type {DOM Element} * @private */this.draggedElement_=document.getElementById(id);// the element to be made draggable must have CSS property// 'position: absolute;' or 'position: fixed;'this.draggedElement_.style.position="absolute";if(!this.draggedElement_){throw"Draggable.NoElement";}/** * Passing this.startMouseDraggable.bind(this) directly to addEventListener * and removeEventListener is WRONG because this is actually passing an * anonymous function as argument, which has no effect on removeEventListener. * The workaround is as below. Use an event handler object to wrap functions. * @see http://stackoverflow.com/questions/4386300/javascript-dom-how-to-remove-all-events-of-a-dom-object * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind * @enum {function} */this.eventHandlers_={'startMouseDraggable':this.startMouseDraggable.bind(this),'mouseDrag':this.mouseDrag.bind(this),'releaseElement':this.releaseElement.bind(this)}/** * The initial X position of draggable DOM element. * @type {number} * @private */this.startX_=undefined;/** * The initial Y position of draggable DOM element. * @type {number} * @private */this.startY_=undefined;/** * The initial X position of mouse cursor of mouse down event. * @type {number} * @private */this.initialMouseX_=undefined;/** * The initial Y position of mouse cursor of mouse down event. * @type {number} * @private */this.initialMouseY_=undefined;// start to listen to mouse down event of draggable elementaddEventListener(this.draggedElement_,'mousedown',this.eventHandlers_.startMouseDraggable);};/** * start DOM element draggable mouse event. * @param {Object} e The event object passed by browser automatically in W3C- * compliant browser. For IE, use 'e || window.event'. * @private */Draggable.prototype.startMouseDraggable=function(e){varevt=e||window.event;// For IE compatible// suppress the default action of the mouse event: start selecting text.// maybe no need to 'return false;' at the end of this function.// @see http://stackoverflow.com/questions/1000597/event-preventdefault-function-not-working-in-ieif(evt.preventDefault)evt.preventDefault();if(evt.stopPropagation)evt.stopPropagation();if(window.event)evt.returnValue=false;// IE version of preventDefault// In case mouse move and up event have been previously registeredthis.releaseElement();// Set current position of dragged elementthis.startX_=this.draggedElement_.offsetLeft;this.startY_=this.draggedElement_.offsetTop;// Set current position of mouse cursorthis.initialMouseX_=evt.clientX;this.initialMouseY_=evt.clientY;/** * From 'Drag and drop - QuirksMode': * However, the mousemove and mouseup event should be set not on the element, * but on the entire document. The reason is that the user may move the mouse * wildly and quickly, and he might leave the dragged element behind. If the * mousemove and mouseup functions were defined on the dragged element, the * user would now lose control because the mouse is not over the element any * more. That's bad usability. */addEventListener(document,'mousemove',this.eventHandlers_.mouseDrag);addEventListener(document,'mouseup',this.eventHandlers_.releaseElement);/** * From 'Drag and drop - QuirksMode': * suppress the default action of the mouse event: start selecting text. * * return false = evt.preventDefault + evt.stopPropagation * @see http://stackoverflow.com/questions/128923/whats-the-effect-of-adding-return-false-to-an-onclick-event * @see http://stackoverflow.com/questions/1357118/event-preventdefault-vs-return-false */returnfalse;};/** * drag DOM element by mouse (mouse move event callback) * @param {Object} e The event object passed by browser automatically in W3C- * compliant browser. For IE, use 'e || window.event'. * @private */Draggable.prototype.mouseDrag=function(e){varevt=e||window.event;// For IE compatible// suppress the default action of mouse event// maybe no need to 'return false;' at the end of this function.// @see http://stackoverflow.com/questions/1000597/event-preventdefault-function-not-working-in-ieif(evt.preventDefault)evt.preventDefault();if(evt.stopPropagation)evt.stopPropagation();if(window.event)evt.returnValue=false;// IE version of preventDefault// calculate the delta of mouse cursor movementvardX=evt.clientX-this.initialMouseX_;vardY=evt.clientY-this.initialMouseY_;this.setPosition(dX,dY);// suppress the default action of mouse eventreturnfalse;};/** * Set new position of dragged element by mouse dragging. * @param {number} dX The delta-X of mouse cursor movement * @param {number} dY The delta-Y of mouse cursor movement * @private */Draggable.prototype.setPosition=function(dx,dy){this.draggedElement_.style.left=this.startX_+dx+'px';this.draggedElement_.style.top=this.startY_+dy+'px';};/** * stop listening to mouse Move and Up event. * @param {Object} e The event object passed by browser automatically in W3C- * compliant browser. For IE, use 'e || window.event'. * @private */Draggable.prototype.releaseElement=function(e){removeEventListener(document,'mousemove',this.eventHandlers_.mouseDrag);removeEventListener(document,'mouseup',this.eventHandlers_.releaseElement);};
Note that the element to be made draggable must have CSS propertyposition: absolute; or position: fixed; (position set to
absolute in the JavaScript code). I put a lot of comments in the code to make
the code understandable. Hope this would be helpful for those who are
interested.
If you need draggable elements in AngularJS way, see [4].