Skip to main content

Search and Top Navigation

#4454 closed enhancement (worksforme)

Opened April 10, 2009 06:48PM UTC

Closed October 12, 2010 02:43AM UTC

Last modified May 19, 2013 04:03AM UTC

Draggable Grid Option Demands "beforeStart" Callback

Reported by: rgrwkmn Owned by:
Priority: minor Milestone: 1.8
Component: ui.draggable Version: 1.7.1
Keywords: start callback Cc:
Blocked by: Blocking:
Description

Since the grid option snaps a dragged element to a grid relative to the element's starting position, it is necessary to reset the position of the dragged element when showing a visible grid on the page that the element is not already aligned with. At the time that the start callback is triggered, the dragger has full control of the draggable, the position of which cannot be programmatically reset.

I worked around this by adding a beforeStart callback in _mouseStart in ui.draggable.js. I put it at the latest possible point in the function where I could still control the draggable's position and get access to the helper. The code is as follows (note that I removed some comments that were messing up this ticket's formatting):

_mouseStart: function(event) {

		var o = this.options;

		//Create and append the visible helper
		this.helper = this._createHelper(event);

		//Cache the helper size
		this._cacheHelperProportions();

		//If ddmanager is used for droppables, set the global draggable
		if($.ui.ddmanager)
			$.ui.ddmanager.current = this;

		/*
		 * - Position generation -
		 * This block generates everything position related - it's the core of draggables.
		 */

		//Cache the margins of the original element
		this._cacheMargins();

		//Store the helper's css position
		this.cssPosition = this.helper.css("position");
		this.scrollParent = this.helper.scrollParent();
		
		//Call beforeStart callback, fired before the 
		//draggables position is in the control of the dragger
		this._trigger("beforeStart", event);

		//The element's absolute position on the page minus margins
		this.offset = this.element.offset();
		this.offset = {
			top: this.offset.top - this.margins.top,
			left: this.offset.left - this.margins.left
		};

		$.extend(this.offset, {
			click: { //Where the click happened, relative to the element
				left: event.pageX - this.offset.left,
				top: event.pageY - this.offset.top
			},
			parent: this._getParentOffset(),
			relative: this._getRelativeOffset()
		});

		//Generate the original position
		this.originalPosition = this._generatePosition(event);
		this.originalPageX = event.pageX;
		this.originalPageY = event.pageY;

		if(o.cursorAt)
			this._adjustOffsetFromHelper(o.cursorAt);

		//Set a containment if given in the options
		if(o.containment)
			this._setContainment();

		//Call plugins and callbacks
		this._trigger("start", event);

		//Recache the helper size
		this._cacheHelperProportions();

		//Prepare the droppable offsets
		if ($.ui.ddmanager && !o.dropBehaviour)
			$.ui.ddmanager.prepareOffsets(this, event);

		this.helper.addClass("ui-draggable-dragging");
		this._mouseDrag(event, true);
		return true;
	}

And this is the code I use to snap the draggable to my grid:

beforeStart: function(event, ui) {
				if (global.snap2grid) {
					var ttop = parseInt(ui.helper.css('top'));
					var left = parseInt(ui.helper.css('left'));
					ui.helper.css({
						top: (ttop - (ttop % global.gridScale))+'px',
						left: (left - (left % global.gridScale))+'px'
					});
				}
			}

I hope this is a useful suggestion, I'd love to see it in an official release. There are probably many uses for this callback and some more thought should go into where it actually happens (I just shoved it in based on my immediate needs). It should be in _mouseStart as opposed to _mouseCapture because someone clicking on a draggable element isn't necessarily going to drag it and that initial click could have any number of meanings on a website.

Attachments (0)
Change History (6)

Changed May 07, 2009 12:42PM UTC by jzaefferer comment:1

milestone: TBD1.next

Changed September 17, 2009 09:31AM UTC by dVyper comment:2

I'd like to make vote that this feature is included in a future release. The solution posted here has proved very useful in my project!

Changed April 01, 2010 11:09AM UTC by sidney comment:3

Another vote for this feature here. This is exactly what my project needed!

Changed October 12, 2010 02:43AM UTC by scottgonzalez comment:4

resolution: → worksforme
status: newclosed

You can change the position of the draggable by modifying the ui.position object in the callbacks. For example, this would force the draggable to always have it's top moving along the 200px line:

$( "#draggable" ).draggable({
    drag: function( event, ui ) {
        ui.position.top = 200;
    }
});

Changed October 03, 2012 04:56PM UTC by scottgonzalez comment:5

milestone: 1.next1.8

Changed May 19, 2013 04:03AM UTC by tj.vantoll comment:6

#9310 is a duplicate of this ticket.