I've successfully implemented jQueryUI draggable, but as soon as I add hammer.js code, the draggable code no longer works.
It is not as soon as I include hammer.js, but as soon as I use the script.
Why is this? How can I get them both to work?
Both the draggable and hammer are applied to .dataCard
and #main
The draggable code works fine here ( with hammer implementation commented out ): http://goo.gl/MO5Pde
Here is an example of the draggable code:
$('#main').draggable({
axis:'y',
revert:true,
start: function(event, ui){
topValue = ui.position.top;
},
drag: function(event, ui){
if(pastBreakpoint === false){
$('#searchInput').blur();
if(topValue > ui.position.top) return false;
if(ui.position.top >= 161){
if(pastBreakpoint === false){
pastBreakpoint = true;
if($('.loadingRefresh').length === 0) $('#main').before('<div class="loadingRefresh"></div>');
else{
$('.loadingRefresh').remove();
$('#main').before('<div class="loadingRefresh"></div>');
}
$('.loadingRefresh').fadeIn();
$('#main').mouseup();
setTimeout(function(){
location.reload();
}, 1000);
}
}
}
}
});
Here is the hammer code uncommented and the draggable code not working: http://goo.gl/994pxF
Here is the hammer code:
var hammertime = Hammer(document.getElementById('main'), {
transform_always_block: true,
transform_min_scale: 0
});
var posX = 0,
posY = 0,
lastPosX = 0,
lastPosY = 0,
bufferX = 0,
bufferY = 0,
scale = 1,
last_scale = 1;
hammertime.on('touch transform transformend', function(ev) {
if ((" " + ev.target.className + " ").indexOf(" dataCard ") < 0) return;
else manageMultitouch(ev, ev.target); });
function manageMultitouch(ev, element) {
switch (ev.type) {
case 'touch':
last_scale = scale;
return;
case 'transform':
scale = Math.min(last_scale * ev.gesture.scale, 10);
break;
}
if(scale <= 0.5) $(element).hide('clip');
if(scale > 1.0) $(element).addClass('focused');
var transform = "translate(" + 0 + "px," + 0 + "px) " + "scale(" + 1 + "," + scale + ")";
var style = element.style;
style.transform = transform;
style.oTransform = transform;
style.msTransform = transform;
style.mozTransform = transform;
style.webkitTransform = transform;
}
I had the same problem in my app, even with touch punch included. I had to do a good research to find what was the problem stopping the jquery ui drag. The problem occurring is a preventDefault set at the event ( only when hammer is included ) changing the result of a trigger method from jquery ui.
Well, lets get back a little bit:
The first method you should see is the _mouseMove()
, which is connected with the mousemove event.
The drag will be trigged only when the condition (this._mouseStart(this._mouseDownEvent, event) !== false)
be true.
_mouseMove: function (event) {
// IE mouseup check - mouseup happened when mouse was out of window
if ($.ui.ie && (!document.documentMode || document.documentMode < 9) && !event.button) {
return this._mouseUp(event);
}
if (this._mouseStarted) {
this._mouseDrag(event);
return event.preventDefault();
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted =
(this._mouseStart(this._mouseDownEvent, event) !== false);
(this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
}
return !this._mouseStarted;
}
The next method will create the helper ( element's clone ), set some css in the element and return true ( value we expect ), unless this._trigger("start", event)
returns false.
_mouseStart: function(event) {
var o = this.options;
//Create and append the visible helper
this.helper = this._createHelper(event);
this.helper.addClass("ui-draggable-dragging");
//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();
this.offsetParent = this.helper.offsetParent();
this.offsetParentCssPosition = this.offsetParent.css( "position" );
//The element's absolute position on the page minus margins
this.offset = this.positionAbs = this.element.offset();
this.offset = {
top: this.offset.top - this.margins.top,
left: this.offset.left - this.margins.left
};
//Reset scroll cache
this.offset.scroll = false;
$.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
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 ($.ui.ddmanager && !o.dropBehaviour) {
$.ui.ddmanager.prepareOffsets(this, event);
}
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 ( $.ui.ddmanager ) {
$.ui.ddmanager.dragStart(this, event);
}
return true;
}
Below is the first _trigger called, its from drag widget.
_trigger: function (type, event, ui) {
ui = ui || this._uiHash();
$.ui.plugin.call(this, type, [event, ui]);
//The absolute position has to be recalculated after plugins
if(type === "drag") {
this.positionAbs = this._convertPositionTo("absolute");
}
return $.Widget.prototype._trigger.call(this, type, event, ui);
}
At this point the result will call another trigger method (this time from the $.Widget) and that's the point where we have the problem.
_trigger: function (type, event, data) {
var prop, orig,
callback = this.options[type];
data = data || {};
event = $.Event(event);
event.type = (type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[0];
// copy original event properties over to the new event
orig = event.originalEvent;
if (orig) {
for (prop in orig) {
if (!(prop in event)) {
event[prop] = orig[prop];
}
}
}
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
}
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false || event.isDefaultPrevented());
Our problem is exactly at this line. More specific the || before event.isDefaultPrevented()
.
When hammer is included the method event.isDefaultPrevented()
is resulting true, once the value is denied before return, the final value would be false.
(Without the hammer included the event.isDefaultPrevented()
returns false as expected.)
Backing in our _moseMouve()
, instead of calling the _mouseDrag()
method it'll invoke _mouseUp()
.
U can see it will unbind the events and call _mouseStop()
.
_mouseUp: function (event) {
$(document)
.unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
.unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
if (this._mouseStarted) {
this._mouseStarted = false;
if (event.target === this._mouseDownEvent.target) {
$.data(event.target, this.widgetName + ".preventClickEvent", true);
}
this._mouseStop(event);
}
return false;
}
If you change the OR (||) operator by an AND (&&) it'll work fine. Off course it's not a little change, I had been testing it and until this moment I haven't find any problem at all. The line would be like this:
return !($.isFunction(callback) && callback.apply(this.element[0], [event].concat(data)) === false && event.isDefaultPrevented());
As I said, its not 100% secure, but until now I didn't find a reason to keep || instead of &&. I'll keep testing it for a few days. Besides I've already sent an email to the lead developer from jquery ui asking about it.
Similar problem for me using it with isomorphic smartclient.
I fixed it by handling the start event and resetting isDefaultPrevented to false
$(element).draggable({
start: function (event, ui) {
event.isDefaultPrevented = function () { return false; }
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With