web-dev-qa-db-fra.com

jQuery Drag and Drop sur les appareils tactiles (iPad, Android)

Nous avons un site Web de jeu de cartes qui utilise intensivement jQuery Draggable & Droppable et qui fonctionne presque sans faille (avec une souris) depuis presque un an.

Nous aimerions VRAIMENT que le site fonctionne sur des appareils à écran tactile, mais nous ne semblons pas pouvoir utiliser les fonctionnalités de glissement et de suppression de jQuery pour fonctionner de manière fiable.

Glisser fonctionne "ok" sauf si la div glissée se trouve à l'intérieur d'un autre élément dom avec n'importe quel type de décalage, marge, remplissage, etc. Si c'est le cas, l'élément glissé est également décalé du doigt de l'utilisateur d'une valeur similaire. Cela ne semble pas très grave, mais cela rend l'interface inutilisable.

Laisser tomber ne semble tout simplement pas fonctionner.

Nous avons recherché différentes options présentées ici sur SO (nous essayerons de mettre à jour ce post avec des liens vers certaines d’entre elles si je le peux), mais aucune ne fonctionne pour nous.

Nous avons également effectué des recherches sur jQuery Mobile, mais cela reste en alpha et semble néanmoins constituer un cadre permettant de faire en sorte qu'un site imite l'interface utilisateur d'un téléphone par rapport à ce que nous recherchons.

La plupart des SO et des publications de Google sur ce sujet semblent disparaître à la fin de 2010, ce qui me laisse penser qu'il est évident que nous sommes peut-être en train de nous manquer.

BTW, la fonctionnalité que nous recherchons est clairement techniquement possible car les bibliothèques YUI pour le glisser-déposer fonctionnent comme prévu. Malheureusement, nous ne pouvons pas nous contenter de refactoriser le site pour passer de jQuery à YUI.

Quelqu'un a une idée? Nous nous contenterions d'une réponse qui ne prend en charge que l'iPad, mais il ne faut vraiment pas demander à refactoriser le site existant.

Merci!

28
Pat

Collez ceci au début de votre fichier .js:

(function ($) {
    // Detect touch support
    $.support.touch = 'ontouchend' in document;
    // Ignore browsers without touch support
    if (!$.support.touch) {
    return;
    }
    var mouseProto = $.ui.mouse.prototype,
        _mouseInit = mouseProto._mouseInit,
        touchHandled;

    function simulateMouseEvent (event, simulatedType) { //use this function to simulate mouse event
    // Ignore multi-touch events
        if (event.originalEvent.touches.length > 1) {
        return;
        }
    event.preventDefault(); //use this to prevent scrolling during ui use

    var touch = event.originalEvent.changedTouches[0],
        simulatedEvent = document.createEvent('MouseEvents');
    // Initialize the simulated mouse event using the touch event's coordinates
    simulatedEvent.initMouseEvent(
        simulatedType,    // type
        true,             // bubbles                    
        true,             // cancelable                 
        window,           // view                       
        1,                // detail                     
        touch.screenX,    // screenX                    
        touch.screenY,    // screenY                    
        touch.clientX,    // clientX                    
        touch.clientY,    // clientY                    
        false,            // ctrlKey                    
        false,            // altKey                     
        false,            // shiftKey                   
        false,            // metaKey                    
        0,                // button                     
        null              // relatedTarget              
        );

    // Dispatch the simulated event to the target element
    event.target.dispatchEvent(simulatedEvent);
    }
    mouseProto._touchStart = function (event) {
    var self = this;
    // Ignore the event if another widget is already being handled
    if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
        return;
        }
    // Set the flag to prevent other widgets from inheriting the touch event
    touchHandled = true;
    // Track movement to determine if interaction was a click
    self._touchMoved = false;
    // Simulate the mouseover event
    simulateMouseEvent(event, 'mouseover');
    // Simulate the mousemove event
    simulateMouseEvent(event, 'mousemove');
    // Simulate the mousedown event
    simulateMouseEvent(event, 'mousedown');
    };

    mouseProto._touchMove = function (event) {
    // Ignore event if not handled
    if (!touchHandled) {
        return;
        }
    // Interaction was not a click
    this._touchMoved = true;
    // Simulate the mousemove event
    simulateMouseEvent(event, 'mousemove');
    };
    mouseProto._touchEnd = function (event) {
    // Ignore event if not handled
    if (!touchHandled) {
        return;
    }
    // Simulate the mouseup event
    simulateMouseEvent(event, 'mouseup');
    // Simulate the mouseout event
    simulateMouseEvent(event, 'mouseout');
    // If the touch interaction did not move, it should trigger a click
    if (!this._touchMoved) {
      // Simulate the click event
      simulateMouseEvent(event, 'click');
    }
    // Unset the flag to allow other widgets to inherit the touch event
    touchHandled = false;
    };
    mouseProto._mouseInit = function () {
    var self = this;
    // Delegate the touch handlers to the widget's element
    self.element
        .on('touchstart', $.proxy(self, '_touchStart'))
        .on('touchmove', $.proxy(self, '_touchMove'))
        .on('touchend', $.proxy(self, '_touchEnd'));

    // Call the original $.ui.mouse init method
    _mouseInit.call(self);
    };
})(jQuery);

Appelez-moi le matin;) (c'est vraiment arrogant, je n'ai pas écrit cette solution, même si j'aimerais bien l'avoir, je le mentionnerais si je me souvenais de l'endroit où je l'avais trouvée, si quelqu'un savait d'où venait ce code, veuillez commenter et créditez cette personne)

UPDATE: Voilà: C'est ici que j'ai trouvé ceci

36
Likwid_T

Je suggère jQuery UI Touch Punch . Je l'ai testé sur iOS 5 et Android 2.3 et cela fonctionne très bien sur les deux.

16
balexand

Vieux fil que je connais .......

Le problème avec la réponse de @likwid_t est qu’il bloque également toute entrée ou tout autre élément devant réagir sur des "clics" (par exemple des entrées), c’est pourquoi j’ai écrit cette solution. Cette solution a rendu possible l’utilisation de toute bibliothèque existante de glisser-déposer basée sur des événements mousedown, mousemove et mouseup sur n’importe quel périphérique tactile (ou cumputer). C'est aussi une solution multi-navigateur.

J'ai testé plusieurs appareils et cela fonctionne rapidement (en combinaison avec la fonction glisser-déposer de ThreeDubMedia (voir aussi http://threedubmedia.com/code/event/drag )). C'est une solution jQuery, vous ne pouvez donc l'utiliser qu'avec des bibliothèques jQuery. J'ai utilisé jQuery 1.5.1 car certaines fonctions plus récentes ne fonctionnent pas correctement avec IE9 et les versions ultérieures (non testées avec les versions plus récentes de jQuery).

Avant vous ajoutez une opération glisser-déposer à un événement vous devez d'abord appeler cette fonction:

simulateTouchEvents(<object>);

Vous pouvez également bloquer tous les composants/enfants pour la saisie ou accélérer la gestion des événements en utilisant la syntaxe suivante:

simulateTouchEvents(<object>, true); // ignore events on childs

Voici le code que j'ai écrit. J'ai utilisé quelques astuces de Nice pour accélérer l'évaluation (voir le code).

function simulateTouchEvents(oo,bIgnoreChilds)
{
 if( !$(oo)[0] )
  { return false; }

 if( !window.__touchTypes )
 {
   window.__touchTypes  = {touchstart:'mousedown',touchmove:'mousemove',touchend:'mouseup'};
   window.__touchInputs = {INPUT:1,TEXTAREA:1,SELECT:1,OPTION:1,'input':1,'textarea':1,'select':1,'option':1};
 }

$(oo).bind('touchstart touchmove touchend', function(ev)
{
    var bSame = (ev.target == this);
    if( bIgnoreChilds && !bSame )
     { return; }

    var b = (!bSame && ev.target.__ajqmeclk), // Get if object is already tested or input type
        e = ev.originalEvent;
    if( b === true || !e.touches || e.touches.length > 1 || !window.__touchTypes[e.type]  )
     { return; } //allow multi-touch gestures to work

    var oEv = ( !bSame && typeof b != 'boolean')?$(ev.target).data('events'):false,
        b = (!bSame)?(ev.target.__ajqmeclk = oEv?(oEv['click'] || oEv['mousedown'] || oEv['mouseup'] || oEv['mousemove']):false ):false;

    if( b || window.__touchInputs[ev.target.tagName] )
     { return; } //allow default clicks to work (and on inputs)

    // https://developer.mozilla.org/en/DOM/event.initMouseEvent for API
    var touch = e.changedTouches[0], newEvent = document.createEvent("MouseEvent");
    newEvent.initMouseEvent(window.__touchTypes[e.type], true, true, window, 1,
            touch.screenX, touch.screenY,
            touch.clientX, touch.clientY, false,
            false, false, false, 0, null);

    touch.target.dispatchEvent(newEvent);
    e.preventDefault();
    ev.stopImmediatePropagation();
    ev.stopPropagation();
    ev.preventDefault();
});
 return true;
}; 

Que fait-il: Au début, il traduit les événements tactiles en événements de souris. Il vérifie si un événement est causé par un élément sur/dans l'élément qui doit être déplacé. S'il s'agit d'un élément d'entrée tel que input, textarea, etc., la traduction est ignorée ou, si un événement de souris standard y est associé, une traduction est également ignorée. 

Résultat: Chaque élément d'un élément déplaçable fonctionne toujours. 

Bonne codage, greetz, Erwin Haantjes

4
Codebeat

J'ai créé un plugin jQuery basé sur la réponse d'Erwinus: https://github.com/RushPL/jquery.touch-mouse

3
RushPL

LikWidT dispose de la solution la plus simple du site Web référencé. Ce serait formidable si l'un des programmeurs de l'interface utilisateur de JQuery pouvait ajouter ce code ou similaire à leur package. JQuery est certainement le paquet le plus simple pour créer du contenu par glisser/déposer pour les développeurs Web. C’est malheureusement un défaut majeur, presque tous les appareils ayant un écran tactile. Je suis en train de coder sur une Surface Pro, ce qui m’ouvre les yeux sur ces problèmes.

Juste pour référencer à nouveau le site: https://github.com/furf/jquery-ui-touch-punch

Edit: Également pour clarifier à quel point ce correctif est simple. Je devais d'abord m'assurer de re-commander mes fichiers d'inclusion de telle sorte que le fichier JQuery Javascript soit d'abord suivi de Jquery UI Javascript puis de mes fichiers CSS. Ensuite, je viens de copier/coller ci-dessous les fichiers include le code Javascript dans le post ci-dessus et c’est tout. Je n'ai modifié aucun code. Il s'agit simplement d'une fonction qui recherche toutes les retouches en temps réel et les convertit en actions équivalentes à la souris. D'où mes affirmations précédentes pour les codeurs JQuery.

0
Willis

Testé sur HTC One M8 sous Android 6.13/Samsung Galaxy Tab S2

function simulateMouseEvent (event, simulatedType) { //use this function to simulate mouse event

// restriction to preserve input use
    window.__touchInputs = {INPUT:1,TEXTAREA:1,SELECT:1,OPTION:1,'input':1,'textarea':1,'select':1,'option':1};
    if( window.__touchInputs[event.target.tagName] ) return ;

} 
0
BigBud52

Given a travaillé pour moi:

eventAfterRender: function (event, element, view ) {
         element.draggable();
},
0
Riddhi Gohil

Vous pouvez essayer ce plugin mais semble fonctionner pour iphone, iPod et ipad. Je ne sais pas si ça résout ton problème. Peut être un spécifique ...

http://code.google.com/p/jquery-ui-for-ipad-and-iphone/

Mais Android cherche toujours une solution.

Faites-moi savoir si cela aide. Cordialement Ricardo Rodrigues

0
Ricardo Rodrigues