Draggable

Page history last edited by Richard D. Worth 8 mos ago

 

type: interaction

release: 1.0

priority: -

status: complete

documentation: http://docs.jquery.com/UI/Draggable

demo: http://jqueryui.com/demos/draggable

 


 

1 - Description:

 

A draggable is behaviour that you can assign to any element. The element can then be repositioned using the mouse, and when used with the Droppable component, dropped on a specific target. The visual and logical behaviour is highly configurable and comes with all kinds of different options.

 


 

2 - Visual Design:

 

N/A

 


 

 

3 - Functional Specifications/Requirements:

 

Options/Methods/Callbacks

  • options:
    • addClasses (Boolean | default: true)
    • appendTo (Element, Selector | default: 'parent')
    • axis (String | default: false)
      • Possibe string values: 'x', 'y'
    • cancel (Selector | default: ':input,option')
    • connectToSortable (Selector | default: false)
    • containment (Selector, Element, String, Array | default: false)
      • Possible string values: 'parent', 'document', 'window'
      • Array values: [x1, y1, x2, y2]
    • cursor (string | default: 'auto')
    • cursorAt (Object | default: false)
      • Object properties: { top, left, right, bottom }
    • delay (Integer | default: 0)
    • distance (Integer | default: 1)
    • grid (Array | default: false)
      • Array values: [x, y]
    • handle (Element, Selector | default: false)
    • helper (String, Function | default: 'original')
      • Possible string values: 'original', 'clone'
      • Function return type: DOMElement
    • iframeFix (Boolean, Selector | default: false)
    • opacity (Float | default: false)
    • refreshPositions (Boolean | default: false)
    • revert (Boolean, String | default: false)
      • Possible string values: 'valid', 'invalid'
    • revertDuration (Integer | default: 500)
    • scope (String | default: 'default')
    • scroll (Boolean | default: true)
    • scrollSensitivity (Integer | default: 20)
    • scrollSpeed (Integer | default: 20)
    • snap (Boolean, Object | default: false)
      • Object properties: { items: Selector, snap: Function, release: Function }
    • snapMode (String | default: 'both')
      • Possible string values: 'outer', 'inner', 'both'
    • snapTolerance (Integer | default: 20)
    • stack (Object | default: false)
      • Object properties: { group: Selector, min: Integer }
    • zIndex (Integer | default: false)
  • methods:
    • (none)
  • callbacks:
    • start (events: mousedown)
    • drag (events: mousemove)
    • stop (events: mouseup)

 

Specifications

  • if the helper is set to 'original', the dragged element must not have position 'static'
  • the mousedown is automatically cancelled (like the cancel option) on resizable handles (.ui-resizable-handle) to not conflict
  • the helper is only appended to options.appendTo if it's not already in the DOM
  • draggable supports relative, fixed and static positioning. 'absolute' is the default, special implementations for the others. 'relative' substracts the current relative position from the result, 'fixed' substracts unwanted scroll offsets
  • on mousedown, the draggable needs to know if it was dropped on a valid target and therefore checks back with droppables/sortables. It's then used for the revert 'invalid/valid' options for instance.
  • start callback: triggers when movement starts via mouse(mousedown).
  • drag callback: triggers during movement via mouse(drag/mousemove).
  • stop callback: triggers when movement stops via mouse(mouseup). Fires after normalization and reverting has happened.

 


 

4 - Markup & Style:

 

     4.1 Initial markup examples

 

     (not applicable)

 

     4.2 Recommended transformed HTML markup

 

     (not applicable)

 

     4.3 Accessibility recommendation

 

    (Detailed recommendations for ARIA, HTML markup, CSS and javascript that will contribute to universal access and full section 508 accessibility for screen readers, mobile devices and any other devices) 

 

     4.4 CSS & Theme

 

    (not applicable) 

 


 

5 - Latest version of plugin:

 

http://jquery-ui.googlecode.com/svn/trunk/ui/ui.draggable.js

 


 

6 - Open issues being discussed

 

(Use this area to place things that we're hashing out like featuresand options we're not sure we should include, questions about how this fits into UI and relates to other widgets and utilities, known problems, whether features should be broken across multiple releases, etc.)

 


 

 

 

 

Comments (7)

profile picture

Richard D. Worth said

at 7:27 am on Jan 29, 2009

I wonder if it might make sense to remove the greedy option in 1.7. Now that we have custom events bubbling (in jQuery 1.3), couldn't we leave it up to a drop callback whether to return false or stop propagation?

profile picture

shamun toha said

at 12:55 pm on May 25, 2009

it would be better if we can do this.

#while using $('#id').append('Yo wassup').draggable({containment: 'parent'}); how can i select 'Yo wassup' by double click or right click to copy the contents? it doesnt work till this version..

profile picture

Carsten Klein said

at 8:41 am on Aug 12, 2009


Just reading through the code and found the following special case test in _mouseCapture

[...]
if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
return false;

Would'nt it be nicer and even more future safe to test for '.ui-do-not-drag' or something similar instead of explicitly testing for '.ui-resizable-handle'?
I mean, there are plenty of use cases for future plugins and other, similar things, that would make it necessary for the draggable plugin to special case
on _mouseCapture.

Having said this, the resizable plugin now would assign the class .ui-do-not-drag to its handles, in order to make it compatible with the draggable
plugin. That way around, future other plugins could reuse the same behaviour instead of the draggable plugin having to be changed once again in
order to support the other plugins.

Besides that, in the same method, there is

//Quit if we're not on a valid handle
this.handle = this._getHandle(event);
if (!this.handle)
return false;

return true;

The latter could be rewritten so, since _getHandle is actually a _getIsValidHandle.

return this._getIsValidHandle

Yielding a new _mouseCapture method

_mouseCapture: function(event) {

if (this.helper || this.options.disabled || $(event.target).is('.ui-do-not-drag'))
return false;

//Quit if we're not on a valid handle
return this._isValidHandle();
},

_getHandle would then be renamed to _isValidHandle since it expresses the exact nature of the method more explicitly as it does only return boolean value.
_getHandle for me implies that it actually returns a drag handle of some sort.

profile picture

Kevin Dalman said

at 12:48 pm on Aug 20, 2009

I'm using Draggable for a 'splitter-bar' within my own widget. The draggable functionality gets 'disabled' in some states, and re-enabled when the state changes back. The issue is that "ui-state-disabled" gets automatically applied to the element when resizing is disabled. This is not in the Draggable widget, so I assume it's done by the widget factory.

The ui-state-disabled class modifies the opacity and mouse cursor. The cursor property is even marked "!important". It's not appropriate to *assume* every draggable-elements should 'appear disabled' just because dragging is temporarily disabled. I could repeatedly create/destroy the draggable, but using disable/enable is more efficient and intuitive, so I prefer to 'fix' the issue rather than use a work-around.

The ui-draggable-disabled class is the appropriate place to format disabled draggable elements because it is *widget-specific*. The ui-state-disabled class is *generic*, so this class should not be changed. It can be 'overridden' for the draggable elements, but this requires very specific CSS. Plus, because the mouse-cursor is marked !important, it's impossible to set the cursor by Javascript as required in my widget because functionality varies based on user-options.

I temporarily handle the issue by immediately removing the ui-state-disabled class after disabling dragging, but this causes an 'opacity flash' as the class is added and then removed.

$E.draggable("disable").removeClass("ui-state-disabled");

Draggable needs an option to address this issue IMO. Disabling 'dragging' should not be assumed to mean the 'element' is disabled.

profile picture

Kevin Dalman said

at 12:56 pm on Aug 20, 2009

Here is an idea for the ui-state-disabled issue...

There should be a way to control what ui-classes are 'auto-added' to widgets - ideally a *global option* available to all widgets. Perhaps the widget factory could have a set of 'state-class defaults', something like...

uiDefaultClass: "ui-state-default"
uiEnabledClass: ""
uiDisabledClass: "ui-state-disabled"
uiHoverClass: "ui-state-hover"

When creating an instance of *any widget*, users could override OR extend these defaults in the options...

$("#myElement").draggable({
uiDisabledClass: ""
, uiEnabledClass: "ui-state-highlight"
})

It would also be possible to change the options for all instances of the widget on the page...

$.ui.draggble.defaults.uiDisabledClass = "";

Widget authors themselves could modify these defaults to suit the widget's specific functionality, with the 'disabled' class being the most obvious one address. PERHAPS this could even be extended to automate the handling of custom widget classes?

defaults: {
uiDisabledClass: "ui-draggable-disabled"
}
- OR -
defaults: {
uiDisabledClass: "ui-state-disabled ui-draggable-disabled"
}

Such a solution would fix my problem and simultaneously extends the factory's ability to customize 'state classes' to suit any type of widget. I realize widgets can have a custom _setData() method, and perhaps override UI defaults this way, but it is superior for this to be built-in so that *every widget* has the same abilities and option syntax.

I posted this here instead of the Widget Factory page because a solution for Draggable is my priority. So even if this widget factory idea is not suitable, Draggable still needs an option to address this issue. It's wrong to assume that disabling 'dragging' also means the 'element' must appear disabled.

profile picture

Richard D. Worth said

at 1:12 pm on Aug 20, 2009

profile picture

Kevin Dalman said

at 12:06 pm on Aug 22, 2009

Thanks Richard. I should have searched the dev-forum first.

You don't have permission to comment on this page.