Bí quyết là sử dụng vùng thả bao phủ toàn bộ trang và lưu vào bộ nhớ cache target
của window.ondragenter
để so sánh với target
của window.ondragleave
.
Thứ nhất, vùng thả:
<style>
div.dropzone
{
/* positions to point 0,0 - required for z-index */
position: fixed; top: 0; left: 0;
/* above all elements, even if z-index is used elsewhere
it can be lowered as needed, but this value surpasses
all elements when used on YouTube for example. */
z-index: 9999999999;
/* takes up 100% of page */
width: 100%; height: 100%;
/* dim the page with 50% black background when visible */
background-color: rgba(0,0,0,0.5);
/* a nice fade effect, visibility toggles after 175ms, opacity will animate for 175ms. note display:none cannot be animated. */
transition: visibility 175ms, opacity 175ms;
}
</style>
<!-- both visibility:hidden and display:none can be used,
but the former can be used in CSS animations -->
<div style="visibility:hidden; opacity:0" class="dropzone"></div>
Mặc dù vùng thả sẽ được bao phủ toàn bộ trang, sử dụng visibility:hidden
hoặc display:none
sẽ giấu nó khỏi tầm nhìn. Tôi đã sử dụng visibility:hidden
để hoạt ảnh CSS có thể được sử dụng để tạo hiệu ứng chuyển tiếp.
Gán sự kiện
<script>
/* lastTarget is set first on dragenter, then
compared with during dragleave. */
var lastTarget = null;
window.addEventListener("dragenter", function(e)
{
lastTarget = e.target; // cache the last target here
// unhide our dropzone overlay
document.querySelector(".dropzone").style.visibility = "";
document.querySelector(".dropzone").style.opacity = 1;
});
window.addEventListener("dragleave", function(e)
{
// this is the magic part. when leaving the window,
// e.target happens to be exactly what we want: what we cached
// at the start, the dropzone we dragged into.
// so..if dragleave target matches our cache, we hide the dropzone.
if(e.target === lastTarget || e.target === document)
{
document.querySelector(".dropzone").style.visibility = "hidden";
document.querySelector(".dropzone").style.opacity = 0;
}
});
</script>
Vì vậy, đây là quá trình: Bạn kéo một tập tin qua cửa sổ, và ngay lập tức window.ondragenter cháy. target
được đặt thành phần tử gốc, <html>
. Sau đó, bạn ngay lập tức bỏ ẩn vùng thả của mình, bao gồm toàn bộ trang. window.ondragenter
sẽ kích hoạt lại, lần này mục tiêu là dropzone của bạn. Mỗi khi sự kiện dragenter
kích hoạt, nó sẽ lưu vào bộ nhớ cache, vì đây sẽ là mục tiêu phù hợp với sự kiện window.ondragleave
cuối cùng kích hoạt khi bạn kéo ra khỏi cửa sổ.
Tại sao tính năng này hoạt động? Tôi không có ý tưởng, nhưng đó là cách để làm điều đó. Điều này là khá nhiều phương pháp làm việc chỉ kích hoạt khi người dùng kéo ra khỏi trang.
Tôi tin rằng nó hoạt động vì một khi dropzone được ẩn, nó sẽ luôn là là mục tiêu cuối cùng. Nó bao gồm mọi pixel của trang, ngay cả thẻ <html>
. Phương pháp này dựa vào việc kéo thả khi thoát khỏi cửa sổ. Thật không may là có một bug in Firefox ngăn nó hoạt động bình thường.Vui lòng bỏ phiếu cho nó để nó sẽ được sửa chữa sớm hơn. Kể từ Firefox 57.0.2, dragleave xuất hiện để kích hoạt đúng cách. Tuy nhiên, một cách giải quyết là cần thiết, kiểm tra document
thay vì các yếu tố cache:
if(e.target === lastTarget || e.target === document)
Here's a JSBin of it in action. Được thử nghiệm trong Chrome, Firefox, Edge và IE11 mới nhất.
Điều này không hoạt động trong Firefox, vì event.target được trả về bởi dragleave là tài liệu HTML, không khớp với phần tử được lưu trong bộ nhớ cache cuối cùng (rất có thể là .dropzone). –
@DannyLin Thật không may có một [lỗi trong Firefox] (https://bugzilla.mozilla.org/show_bug.cgi?id=656164) ngăn không cho nó hoạt động đúng cách. Vui lòng bỏ phiếu cho nó để nó sẽ được sửa chữa sớm hơn. – bryc
Ah, vì vậy lỗi này có vẻ đã được sửa? Firefox trả về một cách đáng tin cậy 'document' như' event.target' khi rời khỏi cửa sổ. Tôi đưa giải pháp này vào câu trả lời. – bryc