It doesn’t have all the fancy callbacks and options, but hey, it makes things draggable (and with a specified handle optionally).
(function($) {
$.fn.drags = function(opt) {
opt = $.extend({handle:"",cursor:"move"}, opt);
if(opt.handle === "") {
var $el = this;
} else {
var $el = this.find(opt.handle);
}
return $el.css('cursor', opt.cursor).on("mousedown", function(e) {
if(opt.handle === "") {
var $drag = $(this).addClass('draggable');
} else {
var $drag = $(this).addClass('active-handle').parent().addClass('draggable');
}
var z_idx = $drag.css('z-index'),
drg_h = $drag.outerHeight(),
drg_w = $drag.outerWidth(),
pos_y = $drag.offset().top + drg_h - e.pageY,
pos_x = $drag.offset().left + drg_w - e.pageX;
$drag.css('z-index', 1000).parents().on("mousemove", function(e) {
$('.draggable').offset({
top:e.pageY + pos_y - drg_h,
left:e.pageX + pos_x - drg_w
}).on("mouseup", function() {
$(this).removeClass('draggable').css('z-index', z_idx);
});
});
e.preventDefault(); // disable selection
}).on("mouseup", function() {
if(opt.handle === "") {
$(this).removeClass('draggable');
} else {
$(this).removeClass('active-handle').parent().removeClass('draggable');
}
});
}
})(jQuery);
Usage
$('div').drags();
Demo
See the Pen jQuery Draggable with out jQuery UI by Chris Coyier (@chriscoyier) on CodePen.
I didn’t realize you could drop code pens in pages. That is a really nice feature. Sorry to distract from the point of this post.
Good works so far.
I just wanted to request this, thanks.
(Btw i would move “var $el” and “var $drag” up a few lines, so “var” is only there once, and JSHint won’t cry… maybe the handle could be also deeper in the draggable object, .parent() won’t do it? )
Demo? :\
quick & light-weighted alternative to Jquery UI’s draggable!!!!
Oh thank you, was always trying to find a simple script like that… which is NOT buggy :) now adding some
onDragEnd
andonDragStart
is very simple in this casehmm it kills
select
elemets inside a draggable parent container, the options don’t show up anymore if you click that little triangle :/ However buttons, checkboxes and radiobuttons still work fine.
How would I get this to work on touch-based devices?
Hey! Did you find any solution?
Thanks in advance! :D
ya i’m also looking for a solution to touch screen draggable. ui-draggable breaks when transform is being used on the element : \
In response to people asking about touch support, you might be interested in this simple draggable plugin I’ve made that works with touch and mouse events: https://github.com/grantm/jquery-udraggable/
The touch support comes courtesy of Michael S. Mikowski’s jquery.event.ue plugin.
There is a little bug … If there are two draggable items on the same page, the first time when you click on one of them, the other one moves over it . That happens just at the first click, then everything works just fine as it should … Can anyone tell me how could I fix that?
Is not a bug. Remove set css z-index.
$drag.css(‘z-index’, 1000)
$drag.css(‘z-index’, ‘auto’) work fine !
Great thanks for developer
I’ve solved it, was my mistake, instead of using ids for every draggable item, I set the plugin for a class, and I called it “draggable”, didn’t know that the plugin needs that class to activate/disable the moving on mousedown/mouseup. Sorry, I forgot to comment again how the problem was solved. This is really a good plugin. Is there a way to add a new option to this plugin, to set a button or something, and to be able to move the item just from that button? I have a “draggable panel” with dynamic content, and sometimes needs a vertical scrollbar and when you move the scrollbar moves the panel too. For now I’ve solved the problem by adding a button “Activate/Disable Draggable”
got an error “TypeError: $el.css(“cursor”, opt.cursor).on is not a function”
update jquery or use bind instead of on
Draggables are great fun. I’ve had quite a bit of fun with them – http://grafiks101.com . Now that, ’tis the season’, I would like to see a routine that triggers an action based on drop area. You know, like decorating a Christmas tree, but… if you hang the bulb on the wrong branch, the tree explodes!
Fun stuff and I can’t tell you how much I have learned from css-tricks.com!!
How should i use handle? I have a popup and i want to make header as a handle and whole popup as draggable.
Forgot to mention that I have tried using “$.extend({handle:”.drag_handle”,cursor:”move”}, opt);” but not working.
Sorry Guys….it was my mistake. it’s done.
This draggable trick is awesome! Is there a way to include a little button within the draggable object to close it?
I have “oldschool looking diaolgue boxes” in my website- http://www.stefangougherty.com which are draggable and more importantly closable. Unfortunately the old javascript that enables them conflicted with the !DOCTYPE info in the header, so I had to remove that which was crucial for my site to perform correctly in IE.
Any advice? (I only know basic html and css but have been pretty successful at ‘reverse engineering’ more complicated stuff)
thanks!!!
I get a weird behavior in IExplorer where the cursor changes to then “CAN’t drag here” and the draggin requires me to click away, there will be dragging but with delays.
Ehm, dont you have to unbind mousemove somewhere ? once you start dragging, the triggers never stop. the moving stops because the .draggable class is removed, but the triggers keep on firing ?
*-pike
I really appreciate this code. A few minor criticism / questions…
why set a class on the dragging item? to highlight while drag?
why use drg_w, drg_h vars? these can simply be removed with no effect.
why this blog software dooftastically converts underscores to italics? ;)
why this blog software dooftastically removes leading dashes?
this is awesome! i have a quick question – how can i turn it on and off?
like in draggable we can do $element.draggable(‘option’,’disabled’,true/false);
any way to do that over here?
Does not work with Win8 & IE10!
I came across a problem with this script: try logging something from inside the mouseup function on line 28 and you will see that because the mouseup listener is called inside the mousedown function (line 12), the specific function will not only be called every time the mousemove event (line 23) is triggered but multiplied by every time the mousedown event (line 12) is triggered. This can result in some serious preformance issues after the 4th to 5th time you dragged the box somewhere.
I wrote something similar myself – what do you think of my solution?
Sorry if this is waaaaay late (which it is) but I put in all the code identical to how it is here just to try it out and for some reason it did not turn draggable. Is there some bug?
Just what i needed, thanks so much Chris.
Amazing!
Hey great work!! But can u give me the explanation for this or do u have any documentation regarding this work i m eager to know this, as i m novice to javascript that why asking for all this.
This is good! Works perfectly fine in Chrome browser. But, how can it be modified to work for touch based devices?
Is there a way of this working behind a div element?
Hey chris I’m a huge fan of your tricks. I have 2 DIVs nesting next to each other with same height and width and there is a border between them can u give me a tip how to make that border draggable so the width of each one will change when we drag that border I mean 50% 50% drag the border to the right and for example width changes to 30% 70%
The mousemove events keep firing. You need to trigger
mouseup
using the window object. Just before$drag....parents().on('mousemove', ...
write:Nice Tip Arthur, thanks.
$(window).unbind(‘mouseup’);
dont use mouseup….
This is what I’ve done to this so far. It probably still has a few mistakes in it, but I like it better for my purposes than the jQuery UI dragger.
define(function(require, exports, module) {
(function($) {
$.fn.drags = function(opt) {
var originalZ = null,
thisDrag = this,
pos = null,
dragger, $el, $parents;
function sortConstraint(x) {
if (x != null) {
x.sort();
}
}
function startChildEvents(isActive) {
$this = $(thisDrag);
var onOff = isActive ? "on" : "off";
$drag = (opt.handle === "") ?
$this.toggleClass('draggable', isActive) :
$this.toggleClass('active-handle', isActive).parent().toggleClass('draggable', isActive);
if (originalZ == null) { originalZ = $drag.css('z-index'); }
$parents = $drag.css('z-index', isActive ? 1001 : originalZ).parents();
$parents[onOff]("mousemove", mouseMove);
$el[onOff]("mouseup", mouseUp);
$el.css('cursor', isActive ? opt.cursor : "default");
}
function mouseEnd(e) {
$drag.css('z-index', originalZ);
startChildEvents(false);
var movedLeft = (pos.current.left - pos.start.left) < 0;
if(opt.handle === "") {
$(this).removeClass('draggable');
} else {
$(this).removeClass('active-handle').parent().removeClass('draggable');
}
if (opt.autoEnd) {
dragger.start(false);
}
opt.onEndMove({ movedLeft: movedLeft })
}
function mouseDown(e) {
startChildEvents(true);
pos.current = $drag.offset();
pos.start = pos.current;
pos.click = { y: e.pageY, x: e.pageX };
e.preventDefault(); // disable selection
}
function mouseMove(e) {
var endMove = (opt.xMaxMove != null && Math.abs(pos.current.left - pos.start.left) > opt.xMaxMove);
pos.current = {
top: (opt.axis == "x" ? pos.start.top : pos.start.top + e.pageY - pos.click.y),
left:(opt.axis == "y" ? pos.start.left: pos.start.left + e.pageX - pos.click.x)
};
if (opt.xConstrain != null) {
var xDiff = pos.current.left - pos.reference.left;
if (xDiff < opt.xConstrain[0]){
pos.current.left = pos.reference.left + opt.xConstrain[0];
endMove = true;
} else if (xDiff > opt.xConstrain[1]) {
pos.current.left = pos.reference.left + + opt.xConstrain[1];
endMove = true;
}
}
endMove = (endMove || opt.yMaxMove != null && Math.abs(pos.current.top - pos.start.top) > opt.yMaxMove);
if (opt.yConstrain != null) {
var yDiff = pos.current.top - pos.reference.top;
if (yDiff < opt.yConstrain[0]){
pos.current.top = pos.reference.top + opt.yConstrain[0];
endMove = true;
} else if (yDiff > opt.yConstrain[1]) {
pos.current.top = pos.reference.top + + opt.yConstrain[1];
endMove = true;
}
}
$d = $('.draggable');
if (endMove) { mouseEnd(); } else {
$d.offset(pos.current);
}
}
function mouseUp(e) {
$(this).removeClass('draggable').css('z-index', originalZ);
}
dragger = {
start: function(start, newPos) {
if (pos == null) {
var cc = $el.offset();
pos = { start: cc, current: cc, reference: cc };
}
if (typeof newPos != "undefined") {
pos = $.extend(pos, newPos);
}
$el[start ? "on" : "off"]("mousedown", mouseDown);
if (! start) {
startChildEvents(false);
}
}
}
opt = $.extend({
handle:"",
cursor:"move",
axis: null,
autoStart: true,
autoEnd: false,
xMaxMove: null,
yMaxMove: null,
xConstrain: null,
yConstrain: null,
allowDrag: function() { return true; },
onEndMove: function() {}
}, opt);
sortConstraint(opt.xConstrain);
sortConstraint(opt.yConstrain);
$el = (opt.handle === "") ? $el = this : this.find(opt.handle);
$el.dragger = dragger;
if (opt.autoStart) { dragger.start(true); }
return $el
}
})(jQuery);
});
An update to the previous code. This one now can be passed a click event that runs if the mouse drag is just a couple of pixels or less. Both of these use simpler math than the original posted here, but the overall structure is very similar to the original. It has a lot more features than the original as well, but the way that it handles constraints is much easier to deal with than the jqueryUI dragger.
There’s an inherent problem with dragging a panel with input[type=range] controls on it, namely, you can’t use the range sliders because the mousedown event will get intercepted and instead the panel will drag. So I wrote a little solution: dragsOff, using JQuery’s hover method.
And of course, dragsOff:
Nice!
Hi there,
I had the same problem (about the range input) but I didn’t want to use Jquery. What I made is simply to change the draggable attribute on mouse down.
And then:
To make working input elements (text, textarea, range sliders …) within the draggable container simply remove the statements e.preventDefault();
Brilliant! Thank you very much, been looking for this for hours… :)
i understand the above code to drag a image , what if i want a video streaming (webrtc client video) to be made draggable
I’ve edited the script to support touch events… not bullet proof I guess, but working for me.
http://pastebin.com/wVzEqG7i
Sir how to disable draggable when selecting texts in input element inside the draggable div? thank you!
How can I specify a handler? I am new in jQuery and I did not understant your explanation. Thanks
I needed to use this on an element with links. In the original, when the drag completes, it follows a link if that’s where the user grabbed to start/end the drag. This version adds just a few lines to prevent following a link if the (accidental) move was very slight. It also enables it to work on elements positioned with bottom or right in their CSS.
http://codepen.io/swinggraphics/pen/cxmbj
How to add easing for this?
This code has a severe performance problem which you can see if you drag the object several times, each time lagging more and more.
This is due to a new mousemove event handler being bound ‘over the top’ each time the user clicks on the dragged object. If you dragged the object 10 times, there will be 10 redundant mousemove handler tracking the cursor, hence the lag.
Geoffrey Swenson’s code remedies this problem, as it cleans up the event handler after each time the user stops dragging. I’d recommend anyone to use his snippet instead of the original.
Take a look into my solution – https://gist.github.com/NazarkinRoman/12654be37a35ca006375
There is a just fixed version of code. Not rewrited at all, so a usage still the same.
Textarea inside of draggable element becomes uneditable, had to switch back to UI’s draggable().
Is there a way to restrict the draggable element to it’s parent div size?
So that it cannot be dragged off the screen?
jQuery UI can do that. I feel like as soon as you need some fancy extra stuff, you’re better off going with a more robust thing like jQuery UI or Dragabilly than this simple code above.
So, everytime a “mousedown” event is called it will add an event handler of “mousemove” ?
The code works so fine.. But i just want to add a click event that should be prevented when the element is dragged. Can anyone help me out with a simple solution?
A little optimze, remove useless width and height, better event handler
and handler only attach to document instead of all of that’s parents
Demo http://jsbin.com/vuhasika/1/edit
Both of these versions need to end the drag function with
return this;
or it is not chainable.And if you test it with more than one on a page, you find that they are all moved to the location of the first one when dragging.
very well thank you
I think this will work better and faster than jquery ui
This is a great piece of code. Exactly what I needed.
I’d like to suggest a fix though:
If the option {handle: ___} is used then it derails the jQuery chaining because it switches over to the handle element, instead of staying on the same element.
To fix that change this line:
return $el.css('cursor', opt.cursor).on("mousedown", function(e) {
to
$el.css('cursor', opt.cursor).on("mousedown", function(e) {
and change the end lines to this:
Haven’t needed anything alike since the DHTML era, and I’m surprised there’s so few lightweight (light–lined) solutions to this. Thanks to Chris and the wonderful commenters, here follows yet another take, based primarily on Wing‘s optimizations but tweaked for multiple elements, as opposed to his original.
Handler and cursor options have been removed since I saw no purpose for them in my project, but as a bonus it works well with elements with set bottom or right attributes.
also available on Gist: https://gist.github.com/Arty2/11199162
comments and suggestions are welcome of course
This demo works great. But when i implement it on my page, when i drag the element, the ghosted drag item is a huge part of the page, not just the LI that’s being dragged. I’ve also noticed that if i have a lot of these draggable items, other functionality on the page (reorderlist) gets really really slow. Any thoughts?? Thanks!
Update-Issue 1 was fixed by removing a child that had an absolute position within the dragged LI.
cannot not work for click function in dragble div areas
This is beautiful. Awesome work
nice code, thnx alot for share it. one question:
often, u have a container div and many child div inside it, something like ‘header’,’main’, ‘footer’ and …
is there a way to drag hole them (container and it’s child) when drag one of them such as ‘header’ div ?
Note that the original code above works great, EXCEPT that each drag you make slows the browser more and more. To fix this add an
.off('event')
right before each.on
callWhat would the code be to drag WordPress Posts, Pages and Categories up or down in their lists? I am looking for code to add to a custom functions plugin I am building. (Instead of using an existing plugin it would be nice to get the code needed for this and the CSS as well.)
One step further (if that is alright).
Closing a tree branch. If the user has lots of children it would be nice to close a branch to save on the space. For instance having the possibility to close a page branch, categories branch etc.
This piece of code works perfectly for me. I’ve one question.
Is there any way to work this in a mobile device without using jQuery Mobile ??
Like many before me, I have a take on this as well. With touch support included.
Even though the structure of the code is very similar, I actually did not notice this post until recently. But it has pushed me over the edge to start learning how to write jQuery library extensions. So far, it’s only a function (not a plugin) :
http://codepen.io/Shikkediel/pen/ZYbWzL
Reading the comments, I think Arthur’s tip on unbinding the mousemove was worthwhile so I’ve implemented that as well. For most of the other issues mentioned, I think a solution was added in the script.
Previously I resorted to the necessary parts of jQuery UI for creating a draggable, combined with Touch Punch in the hope of cross browser compatibility.
Nice to see that this version actually works on my phone, but with multiple draggable items on the page, they are all moved when one is moved.
I’ve started with Heracles version, and ended up with working iOS and Android version. See the code:
This seems perfect, thank you!
This one works, even on my phone, but it is annoying to have what looks like the item moving down the page a little, after the drag. It’s really the transform scale(1.1). You can’t be precise in dragging if it changes size.
Also, as in the code that this was based on, you are creating a global variable called stack.
On mobile, this version is more difficult to use on small items because of the check for small movements. I kept scrolling the page instead of dragging the item.
It seems that performance isn’t so good, it laggs…
Thank slavap for the code it was very useful. But how can I detect a click event, I’m creating a date scroll (like mobiscroll one) and I want to return the value of the item I clicked.
Not exist a method for not sovrapone elements draggable?
I’m using this plugin but it has a major bug when the element you set as draggable has element. The select just stops working (at least on Chrome). Also I don’t like the UX problem that when the user “starts the drag” on top of an input or button element the drag executes and then the mouse click is executed.
I prefer to ignore some elements from starting the drag event so I forked this plugin to have a cancel option, similar to jquery-ui: https://github.com/pjfsilva/dte-project/blob/master/jquery-draggable/draggable.js
Now works and the UX is improved. By default, cancel elements are: a,input,textarea,button,select,option
Have u a demo link?
Perhaps someone could actually go ahead and create a drag and drop of posts and pages in WordPress. If someone feels the calling…..check out:
https://core.trac.wordpress.org/ticket/2702
http://easywebdesigntutorials.com/drag-and-drop-page-ordering-in-a-tree-page-view-wordpress/
it’s short but powerful
I have a working demo of my version on github https://github.com/geewhizbang/jQueryDragger
This one doesn’t work for me. With multiple draggable items on a page, dragging one moves them all to the first one’s position. And even after a mouseup, they continue to follow the cursor.
And it doesn’t handle touch events.
jquery ui works on mobile this not. cannot compare