HTML5 拖放 - 不适用于 iOS 12.1.2(Safari 和 Chrome)

HTML5 Drag'n'Drop - Not Working on iOS 12.1.2 (Safari and Chrome)

背景

我有一个可通过拖放进行排序的列表。它在桌面浏览器上完美运行,在 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(直接嵌入尺寸太大)

问题

为什么这不适用于 iOS?我什至无法触发 dragstart 事件。在 Android Chrome 上,长按会触发 dragstart.

我的后备想法是使用 touchstarttouchend 进行长按,然后创建我自己的绝对定位幽灵元素并手动拖动它。当 drag* 事件应该正常工作时,这是很多额外的代码。

我是不是漏掉了什么?

编辑

移动版 Safari 中的 LI 元素有一个 ondragstart 事件作为其原型的一部分。问题是让它开火。

此外,根据 caniuse 的说法,移动版 Safari 应该支持此功能。但是,caniuse 还显示 Android Chrome 不支持,这是不正确的。

不幸的是,我没有 iOS12 的设备(我的设备不支持它),我无法对其进行测试。我不知道为什么 iOS/Apple 支持写道他们在 iOS 11.2 中支持此事件,而在您的新设备上不支持。

替代解决方法

通过此解决方法,您将获得适用于所有现代设备和浏览器的解决方案。这比等待 iOS/Apple.

的支持更好

dragstartdragoverdragleavedrop 事件不适用于 所有 触摸设备。

您必须为触摸事件使用适当的事件名称,例如:

  • touchstart
  • touchend
  • touchcancel
  • touchmove

阅读 the documentation 有关此事件的信息。

In order to provide quality support for touch-based user interfaces, touch events offer the ability to interpret finger (or stylus) activity on touch screens or trackpads.

The touch events interfaces are relatively low-level APIs that can be used to support application specific multi-touch interactions such as a two-finger gesture. A multi-touch interaction starts when a finger (or stylus) first touches the contact surface. Other fingers may subsequently touch the surface and optionally move across the touch surface. The interaction ends when the fingers are removed from the surface. During this interaction, an application receives touch events during the start, move and end phases.

Touch events are similar to mouse events except they support simultaneous touches and at different locations on the touch surface. The TouchEvent interface encapsulates all of the touch points that are currently active. The Touch interface, which represents a single touch point, includes information such as the position of the touch point relative to the browser viewport.

不幸的是,事件 touchentertouchleave 已从规范中删除,因此如果您需要它,则必须使用 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
});

也许对于其他一些移动设备及其浏览器,您将需要使用 polyfills (1, 2) 以在移动设备上启用 HTML5 拖放支持 (触摸)设备。

我在这里 fork 你的 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 中工作的是添加一个 polyfill,描述如下: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>

希望对您有所帮助。

过去一周我对这个主题做了很多研究,也做了很多测试和调试。我发现 iOS 支持拖动事件,但 iOS 不会触发它们。(截至 iOS 12)

如果您在 iOS Safari 上测试网页,您会看到所有 HTML 元素的原型都附加了 ondragstart。拖动事件都在那里。他们只是永远不会被触发。

其他人也 运行 关注这个问题。由于此错误支持,Event Modernizer doesn't detect Drag and Drop support

另外,我浏览了 Safari 文档,found this:

iOS Note: Although drag and drop are not supported, you can produce...

它说“支持”,但紧接着下面有一个事件 table,它显示 drag 并表示它不是“已生成”。

这意味着 caniuse 目前是错误的,需要更新。


直接回答问题

代码无法正常工作,仅仅是因为 Apple 选择不触发 拖动事件。