HTML5 Drag'n'Drop - не работает на iOS 12.1.2 (Safari и Chrome) - PullRequest
0 голосов
/ 17 января 2019

Фон

У меня есть список, который можно отсортировать с помощью перетаскивания. Он отлично работает на настольном браузере и на Chrome на Android. Тем не менее, он не работал вообще в Safari и Chrome на iOS 12.1.2 (iPhone 8).

Текущий код

См. Фрагмент ниже, а также для удобного мобильного тестирования: https://codepen.io/Kelderic/pen/KJMRgb

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;
  }
}
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;
}
<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>

Видео поведения на iOS

https://imgur.com/a/I8hzPxC (размер слишком велик для прямого встраивания)

Вопрос * * 1023 Почему это не работает на iOS? Я даже не могу запустить событие dragstart. В Android Chrome при длинном нажатии запускается dragstart. Моя запасная идея - сделать длинное нажатие, используя touchstart и touchend, затем создать собственный абсолютно призрачный элемент-призрак и перетащить его вручную. Это много лишнего кода, когда события drag* должны просто работать. Я что-то упустил? Редактировать

Элементы LI в мобильном Safari имеют событие ondragstart как часть своего прототипа. Вопрос в том, чтобы заставить его стрелять.

Также, согласно caniuse, мобильный Safari должен поддерживать это. Однако caniuse также показывает, что Android Chrome не поддерживает, что не соответствует действительности.

Ответы [ 3 ]

0 голосов
/ 29 января 2019

Я разобрал ваш тестовый код codepen здесь: https://codepen.io/bobacus/pen/MLjZMg

Мне было интересно, нужно ли добавлять слушателей к элементам <li>, поэтому изменил часть кода:

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);
      }
    });
}

Но это не сработало.

Я попробовал несколько разных вещей, но в Safari наконец-то он заработал, добавив описанный здесь полифилл: https://www.codeproject.com/Articles/1091766/Add-support-for-standard-HTML-Drag-and-Drop-operat

Я добавил это в начало фрагмента HTML:

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

Надеюсь, это полезно.

0 голосов
/ 30 января 2019

За прошедшую неделю я провел много исследований по этой теме, а также провел много испытаний и отладок. Я обнаружил, что iOS поддерживает события перетаскивания, но iOS не вызывает их. (Начиная с iOS 12)

Если вы протестируете веб-страницу в iOS Safari, вы увидите, что все элементы HTML имеют ondragstart, прикрепленные к их прототипу. События перетаскивания все там. Они просто никогда не сработают.

Другие тоже сталкивались с этой проблемой. Event Modernizer не обнаруживает поддержку Drag and Drop из-за этой ложной поддержки.

Кроме того, я просмотрел документы Safari, и нашел это :

iOS Примечание: хотя перетаскивание не поддерживается, вы можете создать ...

Там написано «поддерживается», но сразу же ниже есть таблица событий, которая показывает drag и говорит, что она не «Создана».

Это означает, что caniuse в настоящее время неверен и нуждается в обновлении.


Прямой ответ на вопрос

Код не работает только потому, что Apple решила не запускать события перетаскивания.

0 голосов
/ 22 января 2019

К сожалению, у меня нет устройства с iOS12 (мое устройство не поддерживает его), и я не могу его протестировать. И я не знаю, почему поддержка iOS / Apple пишет, что они поддерживают это событие в iOS 11.2 и на вашем новом устройстве это не поддерживается.

Альтернативное решение

С помощью этого обходного пути вы получите решение для всех современных устройств и браузеров. Это лучше, чем ждать поддержки от iOS / Apple.

События

dragstart, dragover, dragleave, drop не работают на всех сенсорных устройствах.

Вы должны использовать соответствующие имена событий для сенсорных событий, например:

  • touchstart
  • touchend
  • touchcancel
  • touchmove

Чтение документации об этих событиях.

Чтобы обеспечить качественную поддержку сенсорных пользовательских интерфейсов, сенсорные события предоставляют возможность интерпретировать действия пальцев (или стилуса) на сенсорных экранах или трекпадах.

Интерфейсы сенсорных событий - это API-интерфейсы относительно низкого уровня, которые можно использовать для поддержки специфических для приложений взаимодействий с несколькими касаниями, таких как жест двумя пальцами. Взаимодействие с несколькими касаниями начинается, когда палец (или стилус) впервые касается поверхности контакта. Другие пальцы могут впоследствии касаться поверхности и, необязательно, перемещаться по поверхности касания. Взаимодействие заканчивается, когда пальцы удалены с поверхности. Во время этого взаимодействия приложение получает сенсорные события во время фазы запуска, перемещения и завершения.

События касания аналогичны событиям мыши, за исключением того, что они поддерживают одновременные касания и находятся в разных местах сенсорной поверхности. Интерфейс TouchEvent инкапсулирует все активные точки касания. Сенсорный интерфейс, представляющий одну точку касания, содержит такую ​​информацию, как положение точки касания относительно окна просмотра браузера.

К сожалению, события touchenter и touchleave были удалены из спецификации, и из-за этого, если вам это нужно, вы должны написать обходной путь, используя document.elementFromPoint(), например:

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
});

Возможно, для некоторых других мобильных устройств и их браузеров вам потребуется использовать полифилы ( 1 , 2 ), которые поддерживают HTML5 поддержка перетаскивания на мобильных (сенсорных) устройствах.

...