Jquery-ui sortable doesn't work on touch devices based on Android or IOS
Asked Answered
F

5

28

Is there any fix to make Jquery-ui sortable work on touch devices based on Android or IOS?

Fudge answered 19/7, 2011 at 9:36 Comment(0)
C
29

The other answer is great but unfortunately it will only work on iOS devices.

Also there was is a breakage caused by later versions of jquery.ui that meant that _touchEnd events were not correctly resetting an internal flag (mouseHandled) in mouse.ui and this was causing exceptions.

Both of these problems should now be fixed with this code.

/*
 * 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;

    $.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 );

            return false;           
        },

        _touchMove: function( event ) {
            this._modifyEvent( event );
            this._mouseMove( event );   
        },

        _touchEnd: function( event ) {
            this.element
            .unbind( "touchmove." + this.widgetName )
            .unbind( "touchend." + this.widgetName );
            this._mouseUp( event ); 
        },

        _modifyEvent: function( event ) {
            event.which = 1;
            var target = event.originalEvent.targetTouches[0];
            event.pageX = target.clientX;
            event.pageY = target.clientY;
        }

    });

})( jQuery );
Constringent answered 22/8, 2011 at 4:27 Comment(9)
Awesome, this appears to work. I do get some console errors in the ipad though Line 62: (TypeError: Result of expression 'event.originalEvent' [undefined] is not an object But it still seems to work. I'll see if I can fix that js errorMerri
This code works, however it conflicts with the regular click (for instance the ok button of a dialog). Is it possible to only execute draggable when the user keeps the finger on the item for 500ms?Sparing
@Sparing - That sounds like an issue I was having too. I found just by removing the call to return false in the above code, that allowed the click events to work as normal on the dialogMerri
@Merri Yes, that fixed the problem. They keep using return false, it does not do what most people think it does.Sparing
This script was almost working, but behaved kind of flaky for me. I finally found an article talking about the mouse position of events and figured out I needed to change the _modifyEvent to change the page like this: event.pageX = target.clientX + document.body.scrollLeft; event.pageY = target.clientY + document.body.scrollTop; - with that tweak it works even if I have scrolled my page. I only tested on iOS - not sure if it works on other mobile devices.Ezar
This broke down for me when using the handle option on sortable. I patched it up by using the _mouseCapture event. My changes are at gist.github.com/2416927Fraenum
@DanielEvans could you tell me how to use this code? Do I have to modify some function in JQuery library ?Blender
@AshikaUmangaUmagiliya You need to include this javascript after you have included jQuery and jQuery-ui.Fraenum
Where I need to use this code. I have same problem, my jQuery-ui sortable not working with mobile.Chincapin
B
36

I suggest jQuery UI Touch Punch. I've tested it on iOS 5 and Android 2.3 and it works great on both.

Brandes answered 13/5, 2012 at 5:5 Comment(3)
Actually, this is the only solution which work for me without any problems.Cortico
I actually have problems using touch-punch. After activating draggable and resizabble on a div, after deactivating it default browser touch events (pinch to zoom, swipe to move the screen, etc) events no longer work if they touch the area of a div that was once resizable or draggable.Skunk
Works like a charmOlivarez
C
29

The other answer is great but unfortunately it will only work on iOS devices.

Also there was is a breakage caused by later versions of jquery.ui that meant that _touchEnd events were not correctly resetting an internal flag (mouseHandled) in mouse.ui and this was causing exceptions.

Both of these problems should now be fixed with this code.

/*
 * 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;

    $.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 );

            return false;           
        },

        _touchMove: function( event ) {
            this._modifyEvent( event );
            this._mouseMove( event );   
        },

        _touchEnd: function( event ) {
            this.element
            .unbind( "touchmove." + this.widgetName )
            .unbind( "touchend." + this.widgetName );
            this._mouseUp( event ); 
        },

        _modifyEvent: function( event ) {
            event.which = 1;
            var target = event.originalEvent.targetTouches[0];
            event.pageX = target.clientX;
            event.pageY = target.clientY;
        }

    });

})( jQuery );
Constringent answered 22/8, 2011 at 4:27 Comment(9)
Awesome, this appears to work. I do get some console errors in the ipad though Line 62: (TypeError: Result of expression 'event.originalEvent' [undefined] is not an object But it still seems to work. I'll see if I can fix that js errorMerri
This code works, however it conflicts with the regular click (for instance the ok button of a dialog). Is it possible to only execute draggable when the user keeps the finger on the item for 500ms?Sparing
@Sparing - That sounds like an issue I was having too. I found just by removing the call to return false in the above code, that allowed the click events to work as normal on the dialogMerri
@Merri Yes, that fixed the problem. They keep using return false, it does not do what most people think it does.Sparing
This script was almost working, but behaved kind of flaky for me. I finally found an article talking about the mouse position of events and figured out I needed to change the _modifyEvent to change the page like this: event.pageX = target.clientX + document.body.scrollLeft; event.pageY = target.clientY + document.body.scrollTop; - with that tweak it works even if I have scrolled my page. I only tested on iOS - not sure if it works on other mobile devices.Ezar
This broke down for me when using the handle option on sortable. I patched it up by using the _mouseCapture event. My changes are at gist.github.com/2416927Fraenum
@DanielEvans could you tell me how to use this code? Do I have to modify some function in JQuery library ?Blender
@AshikaUmangaUmagiliya You need to include this javascript after you have included jQuery and jQuery-ui.Fraenum
Where I need to use this code. I have same problem, my jQuery-ui sortable not working with mobile.Chincapin
C
3

is this meant to replace the mouse.ui js code or to be called after that javascript is loaded? I am unable to get it to work for me on an Android tablet.

EDIT for anyone finding this in the future - got this to work for a Samsung Galaxy Android tablet with the following code:

    /iPad|iPhone|Android/.test( navigator.userAgent ) && (function( $ ) {

var proto =  $.ui.mouse.prototype,
_mouseInit = proto._mouseInit;

$.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 );

        //return false;           
    },

    _touchMove: function( event ) {
        this._modifyEvent( event );
        this._mouseMove( event );   
    },

    _touchEnd: function( event ) {
        this.element
        .unbind( "touchmove." + this.widgetName )
        .unbind( "touchend." + this.widgetName );
        this._mouseUp( event ); 
    },

    _modifyEvent: function( event ) {
        event.which = 1;
        var target = event.originalEvent.targetTouches[0];
        event.pageX = target.clientX;
        event.pageY = target.clientY;
    }

});

})( jQuery );
Chamkis answered 30/9, 2011 at 14:57 Comment(0)
I
2

I'm using this snippet below in conjunction with jquery-sortable which does allow the drag sort to happen on my iPhone. I am having a problem after I finish the first sort however as any scrolling on the list at all is detected as a drag.

EDIT - see here as well http://bugs.jqueryui.com/ticket/4143 EDIT 2 - I was able to get this working if I use the entire row as the handle. It also fixed a problem I was having where the offset was incorrect after scrolling.

/*
 * A bridge between iPad and iPhone touch events and jquery draggable, sortable etc. mouse interactions.
 * @author Oleg Slobodskoi  
 */
/iPad|iPhone/.test( navigator.userAgent ) && (function( $ ) {

    var proto =  $.ui.mouse.prototype,
        _mouseInit = proto._mouseInit;

    $.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 );

            this._mouseDown( event );

            return false;           
        },

        _touchMove: function( event ) {
            this._modifyEvent( event );
            this._mouseMove( event );   
        },

        _touchEnd: function( event ) {
            this.element
                .unbind( "touchmove." + this.widgetName )
                .unbind( "touchend." + this.widgetName );
            this._mouseUp( event ); 
        },

        _modifyEvent: function( event ) {
            event.which = 1;
            var target = event.originalEvent.targetTouches[0];
            event.pageX = target.clientX;
            event.pageY = target.clientY;
        }

    });

})( jQuery );
Idiographic answered 22/7, 2011 at 19:25 Comment(0)
A
-1

Using Touch Punch is as easy as 1, 2… Just follow these simple steps to enable touch events in your jQuery UI app:

Include jQuery and jQuery UI on your page.

Include Touch Punch after jQuery UI and before its first use.

Please note that if you are using jQuery UI's components, Touch Punch must be included after jquery.ui.mouse.js, as Touch Punch modifies its behavior.

There is no 3. Just use jQuery UI as expected and watch it work at the touch of a finger.

$('#widget').draggable();

Tested on iPad, iPhone, Android and other touch-enabled mobile devices.

Anele answered 1/12, 2022 at 5:7 Comment(1)
jquery.min.js jquery-ui.min.js - jquery.ui.touch-punch.min.jsAnele

© 2022 - 2024 — McMap. All rights reserved.