Friday, 7 February 2014

jquery draggable jumping effect issue on firefox

Problem:
Jquery draggable is an awesome feature used to drag the elements around the page. But it irritates some times with the jumping effect and positioning issues on the Firefox. There are lots of fix provided over internet but when you apply them it breaks in other browsers like chrome.The jumping issue may also happens while resizing.

The issues are already reported here.

Solution1 - normal jumping effect:
Based on the solution provided here, I am reimplementing the code to work with both chrome and Firefox. The given code fixes the problem with Firefox. But again when comes back to chrome it fails. So I added one condition so that this patch will be called only for Firefox browsers. Also the body position attribute is changed to relative for Firefox.

Create a file name patch_draggable.js and copy the below code inside.
Code:
function monkeyPatch_mouseStart() {
// don't really need this, but in case I did, I could store it and chain
var oldFn = $j.ui.draggable.prototype._mouseStart ;
$j.ui.draggable.prototype._mouseStart = function(event) {var o = this.options;function getViewOffset(node) {
var x = 0, y = 0, win = node.ownerDocument.defaultView || window;
if (node) addOffset(node);
return { left: x, top: y };function getStyle(node) {
return node.currentStyle || // IE
win.getComputedStyle(node, "");
}
function addOffset(node) {
var p = node.offsetParent, style, X, Y;
x += parseInt(node.offsetLeft, 10) || 0;
y += parseInt(node.offsetTop, 10) || 0;if (p) {
x -= parseInt(p.scrollLeft, 10) || 0;
y -= parseInt(p.scrollTop, 10) || 0;if (p.nodeType == 1) {
var parentStyle = getStyle(p)
, localName   = p.localName
, parent      = node.parentNode;
if (parentStyle.position != "static") {
x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
y += parseInt(parentStyle.borderTopWidth, 10) || 0;

if (localName == "TABLE") {
x += parseInt(parentStyle.paddingLeft, 10) || 0;
y += parseInt(parentStyle.paddingTop, 10) || 0;
}
else if (localName == "BODY") {
style = getStyle(node);
x += parseInt(style.marginLeft, 10) || 0;
y += parseInt(style.marginTop, 10) || 0;
}
}
else if (localName == "BODY") {
x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
y += parseInt(parentStyle.borderTopWidth, 10) || 0;
}

while (p != parent) {
x -= parseInt(parent.scrollLeft, 10) || 0;
y -= parseInt(parent.scrollTop, 10) || 0;
parent = parent.parentNode;
}
addOffset(p);
}
}
else {
if (node.localName == "BODY") {
style = getStyle(node);
x += parseInt(style.borderLeftWidth, 10) || 0;
y += parseInt(style.borderTopWidth, 10) || 0;

var htmlStyle = getStyle(node.parentNode);
x -= parseInt(htmlStyle.paddingLeft, 10) || 0;
y -= parseInt(htmlStyle.paddingTop, 10) || 0;
}

if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0;
if ((Y = node.scrollTop))  y += parseInt(Y, 10) || 0;
}
}
}

//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($j.ui.ddmanager)
$j.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();

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

$j.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() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
});

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

//Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
(o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));

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

//Trigger event + callbacks
if(this._trigger("start", event) === false) {
this._clear();
return false;
}

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

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

this.helper.addClass("ui-draggable-dragging");
//JWL: Hier vindt de jump plaats
this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position

//If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
if ( $j.ui.ddmanager ) $j.ui.ddmanager.dragStart(this, event);

return true;

};

}
if($.browser.mozilla) {
monkeyPatch_mouseStart();
$("body").css("position", "relative");

}

Call this after calling jquery.min and jquery.ui.min as given below,
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-ui.min-1.10.2.js"></script>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/patch_draggable.js"></script>

Solution2 : jumping at the time of resizing

After putting the above fix the jumping effect I faced in chrome. So the solution is reset the position after the element is re sized.

Code:


$j( "#draggablecorrect" ).resizable({start: function(event, ui) {
$j( "#draggablecorrect" ).css({
position: "relative !important",
top: "0 !important",
left: "0 !important"
});
},
stop: function(event, ui) {
$j( "#draggablecorrect" ).css({
position: "",
top: "",
left: ""
});
}
});

No comments:

Post a Comment