Skip to content

Instantly share code, notes, and snippets.

@joyeecheung
Created September 10, 2014 10:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joyeecheung/71e0e6a16e3a2788cea2 to your computer and use it in GitHub Desktop.
Save joyeecheung/71e0e6a16e3a2788cea2 to your computer and use it in GitHub Desktop.
A simple drag and drop plugin, supporting IE8+, Firefox, Chrome
;(function(window, undefined) {
/**
* some utitlity to help handling compatibility issues
*/
var util = {
/**
* Cross-browser event handler support.
* @param {Object} elem the event target
* @param {Object} type event object
* @param {Function} fn event handler
*/
addEvent: function(elem, type, fn) {
if (elem.addEventListener) {
/* W3C DOM `addEventListener` supported */
elem.addEventListener(type, fn, false);
} else {
/* IE9 or lower */
elem['e' + type + fn] = fn;
elem[type + fn] = function() {
elem['e' + type + fn](window.event);
}
elem.attachEvent('on' + type, elem[type + fn]);
}
},
/**
* Cross-browser event handler support.
* @param {Object} elem the event target
* @param {Object} type event object
* @param {Function} fn event handler
*/
removeEvent: function(elem, type, fn) {
if (elem.removeEventListener) {
/* W3C DOM `removeEventListener` supported */
elem.removeEventListener(type, fn, false);
} else {
/* IE9 or lower */
elem.detachEvent('on' + type, elem[type + fn]);
elem[type + fn] = null;
}
},
/**
* Decide which button of the mouse is pressed.
* Notice that there is no way to determine
* the button pressed for click or dbclick
* if the browser is IE9 or lower.
*
* @param {Object} event event object
* @return {String} the position of the button pressed
* "LEFT", "MIDDLE" or "RIGHT"
*/
buttonOf: function(event) {
if (event.which == null) {
/* IE case */
return (event.button < 2) ? "LEFT" :
((event.button == 4) ? "MIDDLE" : "RIGHT");
} else {
/* All others */
return (event.which < 2) ? "LEFT" :
((event.which == 2) ? "MIDDLE" : "RIGHT");
}
},
/**
* Test if an elememnt is of a class.
* @param {Object} elem element to test
* @param {String} cls class to test
* @return {Boolean} true if the element is of the class
*/
hasClass: function(elem, cls) {
// add spaces so that partial class name won't return true
return (' ' + elem.className + ' ').indexOf(' ' + cls + ' ') > -1;
},
extend: function(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
}
};
/**
* draggable utility
*/
var draggable = {
state: {
dragging: false,
parentX: 0, // the x-coordinate of the parent of the target, relative to the page
parentY: 0, // the y-coordinate of the parent of the target, relative to the page
target: null // the target element
},
option: {
draggedIndex: 100
},
/**
* Event handler to start dragging the object.
*/
drag: function(event) {
var body = document.body;
var documentElement = document.documentElement;
var target = event.target || event.srcElement;
var state = draggable.state;
if (state.dragging === false
&& util.buttonOf(event) === "LEFT"
&& util.hasClass(target, "draggable")) {
state.dragging = true;
// for lower version of IE and firefox, the scrollTop[Left] is on body
var scrollLeft = body.scrollLeft || document.documentElement.scrollLeft;
var scrollTop = body.scrollTop || document.documentElement.scrollTop;
// get the bounding rectangle of the parent
var rect = target.offsetParent.getBoundingClientRect();
// get the parent's coordinate relative to the page
state.parentX = rect.left + scrollLeft;
state.parentY = rect.top + scrollTop;
state.target = target;
target.style.zIndex = "" + draggable.option.draggedIndex;
}
},
/**
* Event handler to stop dragging the object
*/
drop: function(event) {
var state = draggable.state;
if (state.dragging === true) {
state.dragging = false;
state.target.style.zIndex = "auto";
state.target.style.cursor = "auto";
state.target = null;
}
},
/**
* Event handler to move the object so that it follows the cursor
*/
move: function(event) {
var body = document.body;
var documentElement = document.documentElement;
var state = draggable.state;
var target = state.target;
if (state.dragging === true) {
// get the size of the target to center it around the cursor
var width = target.clientWidth;
var height = target.clientHeight;
// for lower version of IE and firefox, the scrollTop[Left] is on body
var scrollTop = body.scrollTop || document.documentElement.scrollTop;
var scrollLeft = body.scrollLeft || document.documentElement.scrollLeft;
// for lower version of IE, there is no pageX or pageY,
// it has to be calculated manually
var pageX = event.pageX || event.clientX + scrollLeft;
var pageY = event.pageY || event.clientY + scrollTop;
// don't use clientX or clientY here because
// it is a wrong reference when the page is scrolled!
// use pageX and pageY instead.
// subtract half of the size to center the target around the cursor
target.style.left = pageX - width/2 - state.parentX + 'px';
target.style.top = pageY - height/2 - state.parentY + 'px';
target.style.cursor = "move";
}
},
init: function(opt) {
var body = document.body;
// the events must be bound to the body
// so that the object is still being moved even when
// the mouse move too fast and leave the object
// hence the event used here are mousedown and mouseup
util.addEvent(body, "mousedown", draggable.drag);
util.addEvent(body, "mouseup", draggable.drop);
util.addEvent(body, "mousemove", draggable.move);
util.extend(draggable.option, opt);
}
}
util.addEvent(window, "load", draggable.init);
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment