Opened 11 years ago

Closed 9 years ago

Last modified 6 years ago

#4454 closed enhancement (worksforme)

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.

Change History (6)

comment:1 Changed 10 years ago by Jörn Zaefferer

Milestone: TBD1.next

comment:2 Changed 10 years ago by dVyper

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!

comment:3 Changed 10 years ago by sidney

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

comment:4 Changed 9 years ago by Scott González

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;
    }
});

comment:5 Changed 7 years ago by Scott González

Milestone: 1.next1.8

comment:6 Changed 6 years ago by tj.vantoll

#9310 is a duplicate of this ticket.

Note: See TracTickets for help on using tickets.