这是我想要实现的:
页面上有多个放置区。用户应该能够从其操作系统中拖动文件并将其放入放置区。在拖动期间,放置区会被突出显示。有两种视觉上不同的突出显示类型:“目标”(例如,元素用虚线边框勾勒出来)和“悬停”(例如,元素获得明亮的背景)。
目标突出显示同时适用于所有放置区:
当用户将文件拖动到页面上时,所有放置区都应该使用目标突出显示进行突出显示。 当用户将文件拖到页面外部、取消拖放操作或执行拖放操作时,所有放置区的目标突出显示应该被移除。
悬停突出显示只适用于一个放置区:
当用户将文件拖动到放置区时,该放置区应该使用悬停突出显示进行突出显示。 当用户将文件拖到该放置区之外、取消拖放操作或执行拖放操作时,该放置区的目标突出显示应该被移除。
当用户将文件放置在放置区上时,文件名应该出现在放置区内。
当用户将文件放置在放置区之外的页面上时,所有放置区的突出显示应该被移除,不应发生其他任何情况。具体而言,浏览器不应打开已放置的文件。
解决方案应尽可能优雅:不欢迎使用脏技巧,如使用超时、计算dragenter/dragleave事件并在每个dragover上重新应用突出显示。
解决方案应在主要浏览器的最新版本中工作。
我成功解决的问题
Dropping a file outside the dropzone resulted in the browser opening the file.
Solution:
$(document).on('dragover drop', function (e) { e.preventDefault(); });
Dropping a file into a dropzone generates a
drop
event with target equal to a child of the dropzone rather than the dropzone itself.Solution:
$dropzones.on( 'drop', function (event) { /* ... */ // Find the dropzone responsible for the event $targetDropzone = $(event.target).closest($dropzones); /* ... */ });
Hovering the file over dropzone's children generates multiple
dragleave
events, making the Hover highlighting disappear immediately (the Hover highlighting should be removed from a dropzone when the mouse cursor leaves the dropzone, so it's bound to thedragleave
event).Solution: use the
dragout
event instead ofdragleave
.dragout
is a custom event provided by the jquery.event.dragout plugin. This event won't fire for element's children.
无法检测拖动文件离开
document
或window
的时刻,以便执行“删除目标高亮”命令。自定义
dragout
插件仅设计用于<body>
的子元素。它不适用于document
或window
。官方的
dragstart
和dragend
事件在拖动文件时根本不起作用。这是一种预期行为。:(绑定到
document
的dragenter
和dragleave
事件不仅在鼠标指针进入/离开文档时触发,而且在指针进入/离开文档的子元素时也会触发。更糟糕的是,第一个$(document).on('dragenter')
出现的event.target
可能会出现为一个元素(可以是document
或其子元素),而最后一个$(document).on('dragleave')
的出现可能会出现为不同的元素,因此您无法通过比较event.target
来解决问题。由于这些问题,我未能优雅地跟踪鼠标离开文档的时刻。
我尝试使用旨在解决此问题的draghover插件。我设法使其正常工作(仅在Chrome中测试),但在第一次成功拖放后,它将停止正确工作。请参见失败的尝试here。
虽然无法在视觉上告知,但当文件悬停在文档上方时,
dragenter
事件会被触发多次。因此,高亮显示应该应用多次而不是一次。拖动时鼠标指针应该在落点之外悬停时显示浏览器的标准“不能在此处放置”图标,在悬停在落点上方时显示“可以在此处放置”图标。
更新 2014-03-16 15:30,回复 Ian Bytchek 的答案
嗨,同志!谢谢你详细的回答。不幸的是,你的解决方案存在一些问题。
1.
- 无法检测拖动文件离开文档或窗口的时刻,以便执行“删除目标高亮”命令。
$(document).on('dragleave', …
应该可以解决这个问题,请参见下面的 fiddle。
不,这很糟糕。
假设您在 <body>
上监听 dragenter
和 dragleave
事件。每当拖动鼠标指针悬停在任何元素的边缘上时,会触发两个事件:
dragenter
在<body>
上,并将event.target
设置为悬停的元素;dragleave
在<body>
上,并将event.target
设置为悬停元素的父元素。
.dropzone.target-higlighing
选择器来应用。你通过使用 .target-highlighting .dropzone
选择器巧妙地实现了目标高亮。看一下你的代码:
$('body')
.on('dragenter', function (event) {
$(event.target).addClass('target-highlighting');
event.preventDefault();
})
.on('dragleave drop', function (event) {
$(event.target).removeClass('target-highlighting-class');
event.preventDefault();
})
如果拖放区域位于多个嵌套容器中,将文件拖动到这些容器上会导致目标高亮类从最外层容器迁移到最内层容器。由于您在CSS中使用了
.target-highlighting .dropzone
选择器,看起来目标高亮是固定的,直到您将文件拖动到不是dropzone父元素的元素上时才消失,比如侧边栏或dropzone本身。这是不可接受的。目标高亮应该只在拖放文件到页面上时出现,并在拖出页面或拖放完成(通过放置或取消)时消失。
2.
尽管视觉上无法确定,但在文件悬停在文档上方时,dragenter事件会被多次触发。因此,高亮显示会被应用多次而不是一次。 该事件在鼠标进入某个元素时触发。因此,在拖动页面时,它会进入许多元素并多次触发。为避免这种情况,您需要在每个droparea下面“禁用”所有内容,有两种方法可以实现此目的。 第一种方法是使用CSS指针事件,这是最优雅但最不浏览器友好的解决方案。它适用于大多数最新版本,并且我个人非常喜欢它。 第二种方法是在droparea上创建一个透明覆盖层-鼠标仅会击中该覆盖层而不是下面的元素,这将防止多个拖动进入事件的发生。 这些解决方案可用于触发当鼠标指针位于dropzone内部时应用的Hover高亮显示。(顺便说一句,我已经找到了更优美的解决方案:一个dragout事件插件,请参见上面“已解决问题”部分中的#3。)
但它们对于在鼠标指针位于投放区域内外时应用的目标高亮显示完全不合适。您必须禁用整个页面的鼠标事件(使用
pointer-events: none;
或叠加层),并且投放区域将不再接受投放。3. 拖动期间的鼠标指针应在悬停在投放区域外部时显示浏览器的标准“无法在此处投放”图标,并在悬停在投放区域上方时显示“可以在此处投放”图标。
我不确定这一点,但在MAC上似乎无法更改拖动时的图标,因为它使用特殊的默认图标。我认为这是不可能的,但很想了解其他情况。
我注意到我所要求的在Chrome中已经起作用了!请参见下面的链接。
但Firefox不会更改鼠标指针。 :(
一个更好的样板文件以测试解决方案
以下是翻译:
还有一些看起来不错的库,比如http://www.dropzonejs.com/,虽然我没有使用过,但它们是一个很好的“灵感”来源。
我见过这个插件。它并没有解决上述问题。目标高亮根本没有应用,当你将文件拖到dropzone上时,悬停高亮会闪烁。
此外,我无法使用它,因为我有自己的dropzone实现。例如,我的dropzone允许用户对添加到dropzone中的文件进行排序。我只需要一个处理拖动事件的解决方案。
我的个人建议是采用每个droparea插件的方法,而不是像您的示例中那样采用每个页面的方法。一旦添加了上传逻辑、验证等,这些组件往往会变得非常庞大。
你完全正确。在我的项目中,我使用了精妙的jQuery UI Widget Factory。这是一种定义与彼此独立的jQuery插件行为的方法。
我创建了一个更好的样板文件,用于测试进一步的解决方案:http://jsbin.com/rupaloba/4/edit?html,css,js,output。希望它不会太复杂。
$.event.special
代码风格中,使其更加方便使用。我将结果插件称为“dragbetter”(不是最终名称)。请查看:http://jsbin.com/rupaloba/12/edit 我将在Github上发布它,在这里发布答案,并且,嗯,领取自己的赏金。 - Andrey Mikhaylov - lolmaus