Background
我有一个可以通过拖放进行排序的列表。它在桌面浏览器和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;
}
}
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://i.stack.imgur.com/UDyXF.jpg(尺寸太大,无法直接嵌入)
问题
为什么这在 iOS 上不起作用?我什至无法触发dragstart
事件。在Android Chrome上,长按触发dragstart
。
我的后备想法是使用touchstart
和touchend
进行长按,然后创建我自己的绝对定位的幽灵元素并手动拖动它。当drag*
事件应该正常工作时,这是很多额外的代码。
我错过了什么吗?
编辑
移动 Safari 中的 LI 元素有一个ondragstart
事件作为其原型的一部分。问题是让它开火。
此外,根据caniuse的说法,移动Safari应该支持这一点。但是,caniuse也显示Android Chrome不支持,这是不正确的。
我在这里分叉了你的代码笔测试用例: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
附加到其原型上。拖动事件都在那里。他们只是永远不会被触发。
其他人也遇到了这个问题。由于这种错误支持,事件现代化程序不会检测到拖放支持。
此外,我翻阅了 Safari 文档,发现:
iOS注意:虽然不支持拖放,但您可以产生...
它说"支持",但紧靠下面有一个事件表,它显示了drag
并说它不是"生成的"。
这意味着犬科目前是错误的,需要更新。
直接回答问题
该代码不起作用仅仅是因为Apple选择不触发拖动事件。
不幸的是,我没有装有iOS12的设备(我的设备不支持它),也无法对其进行测试。而且我不知道为什么iOS/Apple支持写道他们在iOS 11.2中支持此事件,而在您的新设备上则不支持。
替代解决方法
使用此解决方法,您将获得适用于所有现代设备和浏览器的解决方案。这比等待iOS/Apple的支持要好。
dragstart
、dragover
、dragleave
、drop
事件并非在所有触摸设备上都有效。
您必须为触摸事件使用专有事件名称,例如:
touchstart
touchend
touchcancel
touchmove
阅读有关此事件的文档。
为了为基于触摸的用户界面提供高质量的支持,触摸事件提供了解释触摸屏或触控板上的手指(或触笔)活动的功能。
触摸事件接口是相对低级的 API,可用于支持特定于应用程序的多点触控交互,例如双指手势。当手指(或触笔)首次触摸接触表面时,多点触控交互开始。其他手指随后可以触摸表面,并可选择在触摸表面上移动。当手指从表面上移开时,交互结束。在此交互期间,应用程序在开始、移动和结束阶段接收触摸事件。
触摸事件与鼠标事件类似,只是它们支持同时触摸和触摸表面上的不同位置。触摸事件接口封装了当前处于活动状态的所有触摸点。表示单个触摸点的触摸界面包括触摸点相对于浏览器视口的位置等信息。
不幸的是,事件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
});
也许对于其他一些移动设备及其浏览器,您将需要使用polyfill(1,2),以便在移动(触摸)设备上启用HTML5拖放支持。