HTML5 Drag'n'Drop - 不适用于 iOS 12.1.2(Safari 和 Chrome)

And*_*cer 7 html javascript webkit drag-and-drop ios

背景

我有一个可通过拖放进行排序的列表。它可以在桌面浏览器和 Android 上的 Chrome 上完美运行。但是,它在 iOS 12.1.2 (iPhone 8) 上的 Safari 和 Chrome 上根本不起作用。

当前代码

请参阅下面的代码段,也可以方便地进行移动测试: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;
  }
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

iOS 上的行为视频

https://imgur.com/a/I8hzPxC(尺寸太大无法直接嵌入)

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

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

我错过了什么吗?

编辑

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

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

And*_*cer 7

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

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

其他人也遇到了这个问题。由于这种错误支持,Event Modernizer不会检测到拖放支持。

此外,我仔细研究了 Safari 文档,发现了这一点

iOS 注意:虽然不支持拖放,但您可以生成...

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

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


直接回答问题

该代码不起作用仅仅是因为 Apple 选择不触发拖动事件。


小智 6

我在这里分叉了你的 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);
      }
    });
}
Run Code Online (Sandbox Code Playgroud)

但这没有用。

我尝试了一些不同的方法,但最终让它在 Safari 中工作的是添加此处描述的polyfill: https: //www.codeproject.com/Articles/1091766/Add-support-for-standard-HTML-Drag-and - 跌落操作

我将其添加到 HTML 片段的顶部:

<script src="http://bernardo-castilho.github.io/DragDropTouch/DragDropTouch.js"></script>
Run Code Online (Sandbox Code Playgroud)

我希望这是有帮助的。


Bha*_*ata 5

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

替代解决方法

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

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

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

  • touchstart
  • touchend
  • touchcancel
  • touchmove

阅读有关此事件的文档

为了为基于触摸的用户界面提供高质量支持,触摸事件提供了解释触摸屏或触控板上手指(或手写笔)活动的能力。

触摸事件接口是相对较低级别的 API,可用于支持特定于应用程序的多点触摸交互,例如两指手势。当手指(或触笔)首次触摸接触表面时,多点触摸交互开始。其他手指随后可以触摸该表面并且可选地在触摸表面上移动。当手指离开表面时,交互结束。在此交互过程中,应用程序在开始、移动和结束阶段接收触摸事件。

触摸事件与鼠标事件类似,不同之处在于它们支持同时触摸并且位于触摸表面的不同位置。TouchEvent 接口封装了当前活动的所有触摸点。触摸界面代表单个触摸点,包括触摸点相对于浏览器视口的位置等信息。

不幸的是,事件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
});
Run Code Online (Sandbox Code Playgroud)

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