Using the code example from Jquery-ui sortable doesn't work on touch devices based on Android or IOS to touch enable jQuery UI sortables on iOS devices, there is a problem when registering a knockout.js click handler as well as the jQuery UI sortable on the same elements. The knockout.js handler doesn't fire on touch enabled devices, but does on desktops/laptops.
Adding a flag called moved it is possible to track when a click handler needs triggered, marked with // TRIGGER HERE
below:
/*
* Content-Type:text/javascript
*
* A bridge between iPad and iPhone touch events and jquery draggable,
* sortable etc. mouse interactions.
* @author Oleg Slobodskoi
*
* modified by John Hardy to use with any touch device
* fixed breakage caused by jquery.ui so that mouseHandled internal flag is reset
* before each touchStart event
*
*/
(function( $ ) {
$.support.touch = typeof Touch === 'object';
if (!$.support.touch) {
return;
}
var proto = $.ui.mouse.prototype,
_mouseInit = proto._mouseInit
moved = true;
$.extend( proto, {
_mouseInit: function() {
this.element
.bind( "touchstart." + this.widgetName, $.proxy( this, "_touchStart" ) );
_mouseInit.apply( this, arguments );
},
_touchStart: function( event ) {
if ( event.originalEvent.targetTouches.length != 1 ) {
return false;
}
this.element
.bind( "touchmove." + this.widgetName, $.proxy( this, "_touchMove" ) )
.bind( "touchend." + this.widgetName, $.proxy( this, "_touchEnd" ) );
this._modifyEvent( event );
$( document ).trigger($.Event("mouseup")); //reset mouseHandled flag in ui.mouse
this._mouseDown( event );
moved = false;
return false;
},
_touchMove: function( event ) {
this._modifyEvent( event );
this._mouseMove( event );
moved = true;
},
_touchEnd: function( event ) {
this.element
.unbind( "touchmove." + this.widgetName )
.unbind( "touchend." + this.widgetName );
this._mouseUp( event );
if (! moved) {
// TRIGGER HERE
}
},
_modifyEvent: function( event ) {
event.which = 1;
var target = event.originalEvent.targetTouches[0];
event.pageX = target.clientX;
event.pageY = target.clientY;
}
});
})( jQuery );
The problem is, how do you trigger a click event to knockout.js from jQuery UI?
I've tried this.element.click()
, this.element.get().click()
, this.element.trigger("click")
, etc to no avail.
Update:
Hacked the code to:
Now it works fine with knockout.js's click event.
/*
* Content-Type:text/javascript
*
* A bridge between iPad and iPhone touch events and jquery draggable,
* sortable etc. mouse interactions.
* @author Oleg Slobodskoi
*
* modified by John Hardy to use with any touch device
* fixed breakage caused by jquery.ui so that mouseHandled internal flag is reset
* before each touchStart event
*
*/
(function( $ ) {
$.support.touch = typeof Touch === 'object';
if (!$.support.touch) {
return;
}
var proto = $.ui.mouse.prototype,
_mouseInit = proto._mouseInit
moved = true,
currentTarget = null;
$.extend( proto, {
_mouseInit: function() {
this.element
.bind( "touchstart." + this.widgetName, $.proxy( this, "_touchStart" ) );
_mouseInit.apply( this, arguments );
},
_touchStart: function( event ) {
if ( event.originalEvent.targetTouches.length != 1 ) {
return false
}
this.element
.bind( "touchmove." + this.widgetName, $.proxy( this, "_touchMove" ) )
.bind( "touchend." + this.widgetName, $.proxy( this, "_touchEnd" ) );
this._modifyEvent( event );
$( document ).trigger($.Event("mouseup")); //reset mouseHandled flag in ui.mouse
this._mouseDown( event );
moved = false;
return false;
},
_touchMove: function( event ) {
this._modifyEvent( event );
this._mouseMove( event );
moved = true;
},
_touchEnd: function( event ) {
this.element
.unbind( "touchmove." + this.widgetName )
.unbind( "touchend." + this.widgetName );
this._mouseUp( event );
if (! moved) {
$(currentTarget).click();
}
},
_modifyEvent: function( event ) {
event.which = 1;
var target = event.originalEvent.targetTouches[0];
currentTarget = target.target;
event.pageX = target.clientX;
event.pageY = target.clientY;
}
});
})( jQuery );
In the code you posted you return false from the touchstart
event. In touch enabled devices the touchstart
event fires first and the click
fired approx 300ms later.
If you return false from an event handler this is the same as calling event.preventDefault()
and event.stopPropagation()
so the touchstart
is effectively cancelling the click. This is not a problem on desktops since touchstart
never fires.
http://jsfiddle.net/madcapnmckay/HkbwV/2/
Possible solutions.
<div data-bind="event : { touchstart: somfunction }"></div>
You may also consider writing a touchOrClick custom binding that detects whether touchstart is available and selectively binds to it or the click event.
Hope this helps.