5
votes

Glisser-déposer HTML5 - Ne fonctionne pas sur iOS 12.1.2 (Safari et Chrome)

Contexte

J'ai une liste qui peut être triée par glisser-déposer. Cela fonctionne parfaitement sur le navigateur de bureau et sur Chrome sur Android. Cependant, cela ne fonctionnait pas du tout sur Safari et Chrome sur iOS 12.1.2 (iPhone 8).

Code actuel

Voir l'extrait ci-dessous, ainsi que pour des tests mobiles faciles: https://codepen.io/Kelderic/pen/KJMRgb

<ul class="sorting">
  <li draggable="true" style="user-drag:element;">List Item 15</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 2</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 3</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 4</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 5</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 6</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 7</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 8</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 9</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 10</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 11</li>
  <li draggable="true" style="-webkit-user-drag:element;">List Item 12</li>
</ul>
ul.sorting {
  padding: 0;
  margin: 0;
  list-style: none;
  max-height: 300px;
  overflow-y: auto;
  box-shadow: inset 0 0 3px 1px rgba(0, 0, 0, 0.2);
}

ul.sorting li {
  padding: 10px;
  border-bottom: 1px solid black;
  user-select: none;
  cursor: move;
}

ul.sorting li:last-child {
  border-bottom: none;
}
var dragging = null;

document.addEventListener('dragstart', function(event) {
  var target = getLI(event.target);
  dragging = target;
  event.dataTransfer.setData('text/plain', null);
  event.dataTransfer.setDragImage(self.dragging, 0, 0);
});

document.addEventListener('dragover', function(event) {
  event.preventDefault();
  var target = getLI(event.target);
  var bounding = target.getBoundingClientRect()
  var offset = bounding.y + (bounding.height / 2);
  if (event.clientY - offset > 0) {
    target.style['border-bottom'] = 'solid 4px blue';
    target.style['border-top'] = '';
  } else {
    target.style['border-top'] = 'solid 4px blue';
    target.style['border-bottom'] = '';
  }
});

document.addEventListener('dragleave', function(event) {
  var target = getLI(event.target);
  target.style['border-bottom'] = '';
  target.style['border-top'] = '';
});

document.addEventListener('drop', function(event) {
  event.preventDefault();
  var target = getLI(event.target);
  if (target.style['border-bottom'] !== '') {
    target.style['border-bottom'] = '';
    target.parentNode.insertBefore(dragging, event.target.nextSibling);
  } else {
    target.style['border-top'] = '';
    target.parentNode.insertBefore(dragging, event.target);
  }
});

function getLI(target) {
  while (target.nodeName.toLowerCase() != 'li' && target.nodeName.toLowerCase() != 'body') {
    target = target.parentNode;
  }
  if (target.nodeName.toLowerCase() == 'body') {
    return false;
  } else {
    return target;
  }
}

Vidéo de comportement sur iOS

https://imgur.com/ a / I8hzPxC (La taille est trop grande pour l'intégration directe)

Question

Pourquoi cela ne fonctionne pas sur iOS? Je n'arrive même pas à déclencher l'événement dragstart . Sur Android Chrome, un appui long déclenche le dragstart .

Mon idée de secours est de faire un appui long en utilisant touchstart et touchend , puis créez mon propre élément fantôme positionné de manière absolue et faites-le glisser manuellement. C'est beaucoup de code supplémentaire lorsque les événements drag * devraient simplement fonctionner.

Suis-je en train de manquer quelque chose?

Modifier

Les éléments LI de Safari mobile ont un événement ondragstart dans le cadre de leur prototype. La question est de le faire démarrer.

De plus, selon caniuse, Safari mobile devrait le supporter. Cependant, caniuse montre également qu'Android Chrome n'est pas compatible, ce qui n'est pas vrai.


5 commentaires

C'est peut-être la vieille triste histoire que cela fonctionne uniquement sur iPad et pas sur iPhone? Avez-vous essayé l'iPad?


Pour ce que ça vaut, l'exemple de codepen ne fonctionne pas pour moi sur Chrome 71.0.3578.98, sur Mac OS X 10.14.2, mais fonctionne sur Firefox. Faire glisser sur Chrome semble n'avoir aucun effet, bien que le DOM soit très subtilement changé - l'attribut style de l'élément

  • reçoit un caractère espace ajouté entre "-webkit-user-drag:" et "element;".


    Oops. Je jouais avec aujourd'hui et j'ai oublié que le stylo était lié ici. J'ai besoin de restaurer le comportement à partir de l'heure de publication de la question.


    @Rob, j'ai corrigé l'exemple CodePen comme code ici. Ce que vous dites il y a quelques minutes était une version WIP qui était cassée dans Chrome en raison de la manipulation du DOM dans l'événement dragstart .


    @Olafant, je n'ai pas testé l'iPad. Je n'en ai malheureusement pas accès.


  • 3 Réponses :


    3
    votes

    Malheureusement, je n'ai pas d'appareil avec iOS12 (mon appareil ne le prend pas en charge) et je ne peux pas le tester. Et je ne sais pas pourquoi le support iOS / Apple écrit qu'il prend en charge ces événements dans iOS 11.2 et sur votre nouvel appareil, il n'est pas pris en charge.

    Solution alternative

    Avec ceci solution de contournement, vous obtiendrez une solution pour tous les appareils et navigateurs modernes. Il vaut mieux attendre le support d'iOS / Apple.

    dragstart , dragover , dragleave , Les événements drop ne fonctionnent pas sur tous les appareils tactiles.

    Vous devez utiliser les noms d'événements appropriés pour vos événements tactiles, comme:

    • touchstart
    • touchend
    • touchcancel
    • touchmove

    Lisez la documentation a > à propos de cet événement.

    Afin de fournir un support de qualité pour les interfaces utilisateur tactiles, les événements tactiles offrent la possibilité d'interpréter l'activité du doigt (ou du stylet) sur les écrans tactiles ou les trackpads.

    Les interfaces d'événements tactiles sont des API de niveau relativement bas qui peuvent être utilisées pour prendre en charge des interactions multi-touch spécifiques à une application, comme un geste à deux doigts. Une interaction multi-touch commence lorsqu'un doigt (ou un stylet) touche pour la première fois la surface de contact. D'autres doigts peuvent ensuite toucher la surface et éventuellement se déplacer sur la surface tactile. L'interaction se termine lorsque les doigts sont retirés de la surface. Au cours de cette interaction, une application reçoit des événements tactiles pendant les phases de début, de déplacement et de fin.

    Les événements tactiles sont similaires aux événements de souris, sauf qu'ils prennent en charge les touches simultanées et à différents endroits sur la surface tactile. L'interface TouchEvent encapsule tous les points de contact actuellement actifs. L'interface tactile, qui représente un point de contact unique, comprend des informations telles que la position du point de contact par rapport à la fenêtre du navigateur.

    Malheureusement, les événements touchenter et touchleave ont été supprimés de la spécification et pour cette raison, si vous en avez besoin, vous devez écrire une solution de contournement en utilisant document.elementFromPoint () comme suit:

    document.addEventListener('touchmove', function(e)
    {
        var touch = e.touches[0],
            elementFromPoint = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset);
    
        if(elem == elementFromPoint) //elem is your element which was entered
        {
            //your code for touchenter event
        }
        else //the code for touchleave event
    });
    

    Peut-être que pour certains autres appareils mobiles et leurs navigateurs, vous devrez peut-être utiliser des polyfills ( 1 , 2 ) qui permettent la prise en charge du glisser-déposer HTML5 sur les appareils mobiles (tactiles).


    3 commentaires

    Salut. Merci d'avoir répondu! Vous vous trompez cependant sur quelques points. Comme je l'ai dit dans ma question, le code glisser-déposer fonctionne parfaitement sur les appareils tactiles Android. Un appui long déclenche le comportement de glissement. C'est seulement iOS que cela ne fonctionne pas. Deuxièmement, le support a été officiellement ajouté à iOS dans iOS 11.2, il devrait donc y fonctionner également, sans avoir à utiliser d'événements tactiles.


    @AndyMercer, salut! Ils pourraient écrire quelque chose, mais aimeriez-vous écrire une solution pour tous les appareils ou peut-être la voulez-vous uniquement pour votre appareil? Si vous ne le souhaitez vraiment que pour votre appareil, je me trompe, mais dans un autre cas, non, car sans cela, vous ne l'obtiendrez pas sur tous les appareils. Malheureusement, je n'ai pas d'appareil avec iOS12 (mon appareil ne le prend pas en charge) et je ne peux pas le tester. Vous pouvez également essayer de lire l'article "Drag and Drop HTML5 natif" < / a> .


    Encore une fois, j'apprécie la réponse. Cependant, étant donné que j'ai écrit le code dans ma question, je comprends clairement l'API HTML Drag and Drop, donc lire des articles à ce sujet n'aidera pas. Ma question est très précise. J'ai un code qui fonctionne sur tous les navigateurs de bureau, fonctionne sur mobile Android et ne fonctionne pas sur mobile iOS. Il devrait, selon ce que j'ai trouvé en ligne, fonctionner sur iOS mobile. La question est donc de savoir pourquoi cela ne fonctionne pas là-bas. Voir caniuse.com/#feat=dragndrop . Il répertorie les mobiles iOS à partir de 11 comme prenant en charge cela.



    3
    votes

    J'ai forké votre cas de test codepen ici: https://codepen.io/bobacus/pen/MLjZMg

    Je me demandais si les écouteurs devaient être ajoutés aux éléments

  • , j'ai donc changé une partie du code:

    <script src="http://bernardo-castilho.github.io/DragDropTouch/DragDropTouch.js"></script>
    

    Mais cela n'a pas fonctionné.

    J'ai essayé plusieurs choses différentes, mais ce qui a finalement réussi à le faire fonctionner dans Safari a été d'ajouter un polyfill décrit ici: https://www.codeproject.com/Articles/1091766/Add- support-for-standard-HTML-Drag-and-Drop-operat

    J'ai ajouté ceci en haut de l'extrait de code HTML:

    listEl = document.getElementById('my_list');
    for (var i = 0; i < listEl.children.length; i ++) {
        itemEl = listEl.children[i];
        itemEl.addEventListener('dragstart', function(event) {
          dragging = getLI(event.target);
          if (dragging) {
              event.dataTransfer.setData('text/plain', null);
              event.dataTransfer.setDragImage(dragging, 0, 0);
          }
        });
    }
    

    J'espère que cela vous sera utile.


    3 commentaires

    L'événement dragstart fait des bulles, donc peu importe qu'il soit attaché aux éléments li ou au wrapper. J'ai essayé quelques polyfills, mais mon objectif était d'éviter d'en utiliser. Il n'y a malheureusement pas d'option, en raison de la façon dont iOS a désactivé les événements de glissement.


    Le code Polyfill a très bien fonctionné pour moi et le HTML5 standard peut être déplacé pour fonctionner comme prévu


    M'a sauvé avec le polyfill. Merci @Rob!



    3
    votes

    J'ai fait beaucoup de recherches au cours de la semaine dernière sur ce sujet, et j'ai fait beaucoup de tests et de débogages. Ce que j'ai découvert, c'est que iOS prend en charge les événements de glissement, mais iOS ne les déclenche pas. (À partir d'iOS 12)

    Si vous testez une page Web sur iOS Safari, vous verrez que tous les éléments HTML ont ondragstart attaché à leur prototype. Les événements de traînée sont tous là. Ils ne sont tout simplement jamais déclenchés.

    D'autres ont également rencontré ce problème. Event Modernizer ne détecte pas la prise en charge du glisser-déposer à cause de cette fausse prise en charge.

    De plus, j'ai parcouru la documentation Safari et a trouvé ceci :

    iOS Remarque: bien que le glisser-déposer ne soit pas pris en charge, vous pouvez produire ...

    Il dit "pris en charge", mais juste en dessous, il y a un tableau d'événements, qui montre glisser et dit que ce n'est pas "généré".

    Cela signifie que caniuse est actuellement erroné et doit être mis à jour.


    Réponse directe à la question

    Le code ne fonctionne pas uniquement parce qu'Apple a choisi de ne pas faire glisser les événements.


    4 commentaires

    Ma réponse est également correcte - vous devez utiliser des événements tactiles. Et maintenant, je ne comprends pas pourquoi je n'ai pas obtenu la prime pour ma réponse? Et pourquoi ma réponse n'a même pas été acceptée ou votée? C'est vraiment injuste!


    @Bharata, la raison pour laquelle je ne vous ai pas décerné la prime est que vous n'avez pas vraiment répondu à la question. Dire que je dois utiliser des événements tactiles n'explique pas pourquoi les événements de glisser ne fonctionnent pas. Safari prend en charge techniquement les événements, car ils font partie de prototypes d'éléments. Cela ne déclenche tout simplement pas ces événements. Votre réponse n'a pas vraiment abordé du tout les événements de traînée. Il n'y a pas eu de recherche, et vous avez admis que vous ne pouviez même pas tester la réponse. J'apprécie la réponse, mais ce n'était pas la réponse.


    J'ai trouvé que cela ne fonctionnait toujours pas avec iOS 13.3 (janvier 2020), mais cela indique toujours qu'il est pris en charge sur caniuse.com ...


    Fait intéressant, j'ai ouvert le codepen attaché à la question, converti la page pour imiter le périphérique Galaxy S5 et la fonction DnD ne fonctionnait pas, mais lorsque le codepen est ouvert à partir d'un mobile Android, cela fonctionne.