接上节
dropEffect属性和effectAllowed属性:
在把元素拖动到放置目标上时,不同类型的元素会导致光标显示为不同的符号;
利用dataTransfer对象,不仅可以传输数据,还能通过它来确定拖动的样式以及放置样式,还能够确定放置时接收什么操作;
dropEffect属性:
dropEffect属性表示拖放操作的视觉效果及行为,用于控制在拖放操作中给用户的反馈,其值如下:
- “none”:不能把拖动的元素放在这里,该值是除了文本框之外所有元素的默认值;
- “move”:应该把拖动的元素移动到放置目标位置;
- “copy”:应该把拖动的元素复制到放置目标位置;
- “link”:表示放置会打开拖动的元素(拖动的元素必须是一个链接,有URL);
该属性应该在放置目标元素的dragenter和dragover事件中设置,设置不同的值时,在放置区域会显示不同的符号,用于告诉用户当前可能的操作,如:
container.addEventListener("dragover", function(event){
event.preventDefault();
// event.dataTransfer.dropEffect = "none";
// event.dataTransfer.dropEffect = "link";
// event.dataTransfer.dropEffect = "move";
event.dataTransfer.dropEffect = "copy";
console.log(event.type + ":" + event.dataTransfer.dropEffect);
},false);
container.addEventListener("dragenter", function(event){
event.preventDefault();
// event.dataTransfer.dropEffect = "move";
console.log(event.type + ":" + event.dataTransfer.dropEffect);
},false);
container.addEventListener("drop", function(event){
event.preventDefault();
console.log(event.type + ":" + event.dataTransfer.dropEffect);
}, false);
当dropEffect属性设置为“none“时,显示禁止符号,同时也不会触发drop事件;
effectAllowed属性:
设置拖拽时应该带有的样式和行为,一般与dropEffect属性配合使用,其表示允许拖动元素的哪种样式和行为(dropEffect),即能够执行哪种放置行为,其可能值如下:
- “uninitialized”:没有给被拖动的元素设置任何放置行为;
- “none”:被拖动的元素不能有任何行为;
- “copy”:只复制;
- “link”:只链接;
- “move”:只移动;
- “copyLink”:复制或链接;
- “copyMove”:复制或移动;
- “linkMove”:链接或移动;
- “all”:允许任意,即复制、移动或链接;
必须在ondragstart事件处理程序中设置effectAllowed属性;官方称默认为值为“all”,实为“uninitialized”;
document.addEventListener("dragover", function(event){
event.preventDefault();
});
var p = document.getElementsByTagName("p")[0];
p.addEventListener("dragstart", function(event){
// event.dataTransfer.effectAllowed = "none";
// event.dataTransfer.effectAllowed = "all";
// event.dataTransfer.effectAllowed = "move";
// event.dataTransfer.effectAllowed = "copy";
// event.dataTransfer.effectAllowed = "link";
// event.dataTransfer.effectAllowed = "copyMove";
// event.dataTransfer.effectAllowed = "copyLink";
event.dataTransfer.effectAllowed = "linkMove";
console.log(event.dataTransfer.effectAllowed);
});
// ...
// 其它类型元素的dragstart事件,并分配不同的值
effectAllowed属性是定为none,则不允许拖放元素,不会触发drop事件;
Firefox默认情况下,拖动链接或图像,或在dragstart事件调用了setData()方法的事件目标元素,都会在新窗口打开,通过drop事件阻止及禁用冒泡解决,也可以使用effectAllowed=”none”禁用;
document.addEventListener("dragstart", function(event){
event.dataTransfer.effectAllowed = "none";
console.log(event.dataTransfer.effectAllowed);
},false);
如此,不仅不会在新窗口打开,连拖放符号也变成禁止符号,但是所有放置元素也没有了放置行为了,即再也不能触发drop事件;
effectAllowed属性在dragstart事件处理程序中设置好后,可以在放置元素的所有拖放事件处理程序中获取,如:
container.addEventListener("dragenter", function(event){
console.log(event.type + ":" + event.dataTransfer.effectAllowed);
},false);
container.addEventListener("dragover", function(event){
event.preventDefault();
console.log(event.type + ":" + event.dataTransfer.effectAllowed);
},false);
container.addEventListener("drop", function(event){
event.preventDefault();
console.log(event.type + ":" + event.dataTransfer.effectAllowed);
}, false);
在dragenter事件中,可以根据不同的值,分别进行相关的处理,如:
container.addEventListener("dragenter", function(event){
event.target.style.border = "solid 2px #00F";
var effect = event.dataTransfer.effectAllowed;
console.log(event.type + ":" + effect);
if(effect == "move"){
event.target.style.borderColor = "#F00";
console.log("移动");
}else if(effect == "copy"){
event.target.style.borderColor = "#0F0";
console.log("复制");
}else if(effect == "link"){
event.target.style.borderColor = "#FF0";
console.log("拖动链接")
}else{
event.target.style.borderColor = "#0FF";
console.log("其他...");
}
},false);
可以在dragover事件中检查effectAllowed属性,以查看哪些操作是允许的,如:
container.addEventListener("dragover", function(event){
if (event.dataTransfer.effectAllowed == "link") {
event.preventDefault();
console.log(event.type + ":" + event.dataTransfer.effectAllowed);
}
},false);
如果dropEffect属性值与effectAllowed属性值一致,会允许放置,即会触发drop事件,并显示effectAllowed指定的符号;否则不允许放置,会显示禁止符号,不会触发drop事件;
p.addEventListener("dragstart", function(event){
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text", event.target.id);
});
link.addEventListener("dragstart", function(event){
event.dataTransfer.effectAllowed = "link";
event.dataTransfer.setData("text", event.target.id);
});
img.addEventListener("dragstart", function(event){
event.dataTransfer.effectAllowed = "copy";
event.dataTransfer.setData("text", event.target.id);
});
container.addEventListener("dragenter", function(event){
event.preventDefault();
event.dataTransfer.dropEffect = "link";
},false);
container.addEventListener("dragover", function(event){
event.preventDefault();
event.dataTransfer.dropEffect = "link";
});
container.addEventListener("drop", function(event){
event.preventDefault();
var dragId = event.dataTransfer.getData("text");
console.log(dragId);
}, false);
如果effectAllowed属性指定是组合值,dropEffect属性值应该是该组合值中的一个,不能使用整个组合值,否则它将使用默认值;
effectAllowed属性设定为all 或不设定,则dropEffect属性允许被设定为任何值;
在IE中,设置dropEffect属性没有效果,但可以通过在dragover事件中,判断effectAllowed与dropEffect属性值是否一致,以决定是否取消dragover事件的默认行为,如:
container.addEventListener("dragover", function(event){
event.dataTransfer.dropEffect = "link";
var isPreventDefault = (event.dataTransfer.effectAllowed == "all" || event.dataTransfer.effectAllowed.indexOf(event.dataTransfer.dropEffect) > -1);
if (isPreventDefault) {
event.preventDefault();
}
},false);
container.addEventListener("drop", function(event){
event.preventDefault();
var dragId = event.dataTransfer.getData("text");
var drag = document.getElementById(dragId);
event.target.appendChild(drag);
}, false);
在drop事件中,可以检查effectAllowed属性,以确定最终执行哪种操作;
container.addEventListener("drop", function(event){
var dragId = event.dataTransfer.getData("text");
var drag;
if (dragId && document.getElementById(dragId)) {
if (event.dataTransfer.effectAllowed == "move") {
drag = document.getElementById(dragId);
event.target.appendChild(drag);
}else if (event.dataTransfer.effectAllowed == "copy") {
drag = document.getElementById(dragId).cloneNode(true);
event.target.appendChild(drag);
}else if (event.dataTransfer.effectAllowed == "link") {
var url = event.dataTransfer.getData("URL");
event.target.appendChild(document.createTextNode(url));
}
}
}, false);
示例:垃圾桶效果
<style type="text/css">
#container {
width: 300px; height: 250px; float: left;
border: solid 1px #000;
}
#container div {
width: 100px; height: 80px; line-height: 80px;
text-align: center; float: left; margin: 20px;
background: url(images/folder.png) no-repeat;
}
div#recycle {
width: 200px; height: 200px; float: left;
text-align: center; margin: 30px;
background: url(images/recycle.png) no-repeat;
}
</style>
<h3>垃圾桶效果</h3>
<!--文件夹-->
<div id="container">
<div draggable="true" id="file1">文件1</div>
<div draggable="true" id="file2">文件2</div>
<div draggable="true" id="file3">文件3</div>
<div draggable="true" id="file4">文件4</div>
</div>
<div id="recycle"></div>
<script type="text/javascript">
var oDiv = document.getElementById("container").getElementsByTagName("div");
var recycle = document.getElementById("recycle");
for (var i = 0; i < oDiv.length; i++) {
oDiv[i].ondragstart = function(event) {
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text", event.target.id);
console.log("开始拖动" + event.target.id);
}
}
recycle.ondragenter = function(event){
event.preventDefault();
event.dataTransfer.dropEffect = "move";
}
recycle.ondragover = function(ev) {
ev.preventDefault();
console.log("可以放置到垃圾桶");
}
recycle.ondrop = function(ev) {
ev.preventDefault();
var id = ev.dataTransfer.getData("text");
var folder = document.getElementById(id);
document.getElementById("container").removeChild(folder);
// oDiv.parentNode.removeChild(oDiv);
}
</script>
示例:设置不同的放置区域
<style>
.cf:before,.cf:after{ content: " "; display: table;}
.cf:after{clear: both;}
.drop-effect {max-width: 960px; margin: 0px auto;}
.reset-button{background: #eee; border: solid 1px #ccc;
padding: 12px 15px; display: inline-block;
cursor: pointer; margin-top: 25px;
}
.drop-effect .drag-zone,
.drop-effect .drop-zone {
width: 100%; background: #eee; border: solid 1px #ccc;
padding: 5px 0 5px 5px; margin-bottom: 1em;
}
.drop-effect .drag-item,
.drop-effect .drop-item {
width: 10%; margin: 0% 0.8% 0.8% 0.6%;
float: left; text-align: center; overflow: hidden;
}
.drop-effect .drag-item .drag,
.drop-effect .drop-item .drop {
border: solid 1px #ccc; background: #DDD;
height: auto; width: 100%; display: inline-block;
transition: all 300ms ease-in; cursor:grab;
}
.drop-effect .drag-zone .drag.drag-active {
border: solid 1px #333; opacity: 0.7;
}
.drop-effect .drop-zone .drop {
width: 100%; height: 0px; padding-bottom: 100%;
}
.drop-effect .drop-zone .drop.drop-active {
border: solid 1px #AAA;
}
.drop-effect .drop-zone .drop.correct {
border: solid 1px #32ce74;
}
.drop-effect .drop-zone img{ width: 100%; height: auto;}
</style>
<div class="drop-effect cf">
<div class="drag-zone cf">
<div class="drag-item">
<h4>Copy</h4>
<img draggable="true" src="images/1.jpg" class="drag" data-effect-allowed="copy" />
</div>
<div class="drag-item">
<h4>Link</h4>
<img draggable="true" src="images/2.jpg" class="drag" data-effect-allowed="link" />
</div>
<div class="drag-item">
<h4>Move</h4>
<img draggable="true" src="images/3.jpg" class="drag" data-effect-allowed="move" />
</div>
<div class="drag-item">
<h4>All</h4>
<img draggable="true" src="images/4.jpg" class="drag" data-effect-allowed="all" />
</div>
<div class="drag-item">
<h4>None</h4>
<img draggable="true" src="images/5.jpg" class="drag" data-effect-allowed="none" />
</div>
<div class="drag-item">
<h4>copyLink</h4>
<img draggable="true" src="images/6.jpg" class="drag" data-effect-allowed="copyLink" />
</div>
<div class="drag-item">
<h4>linkMove</h4>
<img draggable="true" src="images/7.jpg" class="drag" data-effect-allowed="linkMove" />
</div>
<div class="drag-item">
<h4>copyMove</h4>
<img draggable="true" src="images/8.jpg" class="drag" data-effect-allowed="copyMove" />
</div>
</div>
<div class="drop-zone cf">
<div class="drop-item">
<h4>Copy</h4>
<div class="drop" data-drop-effect="copy"></div>
</div>
<div class="drop-item">
<h4>Link</h4>
<div class="drop" data-drop-effect="link"></div>
</div>
<div class="drop-item">
<h4>Move</h4>
<div class="drop" data-drop-effect="move"></div>
</div>
<div class="drop-item">
<h4>None</h4>
<div class="drop" data-drop-effect="none"></div>
</div>
</div>
<div class="reset-button">重置</div>
</div>
<script>
window.onload = function(){
var drag_items = document.querySelectorAll(".drag");
var drop_items = document.querySelectorAll(".drop");
function setUpEventListeners(){
for(var i=0,len=drag_items.length; i<len; i++){
var item = drag_items[i];
item.addEventListener("dragstart", dragStart);
item.addEventListener("drag", drag);
item.addEventListener("dragend", dragEnd);
}
for(var i=0,len=drop_items.length; i<len; i++){
var item = drop_items[i];
item.addEventListener('dragenter', dragEnter);
item.addEventListener('dragover', dragOver);
item.addEventListener('dragleave', dragLeave);
item.addEventListener('drop', drop);
}
}
setUpEventListeners();
var dragItem;
function dragStart(event){
var drag = event.target;
dragItem = event.target;
event.dataTransfer.effectAllowed = drag.getAttribute("data-effect-allowed");
var imageSrc = dragItem.src;
var imageHTML = dragItem.outerHTML;
try {
event.dataTransfer.setData('text/uri-list', imageSrc);
event.dataTransfer.setData('text/html', imageHTML);
} catch (e) {
event.dataTransfer.setData('text', imageSrc);
}
drag.classList.add("drag-active");
}
function dragEnter(event){
event.preventDefault();
var drop = this;
event.dataTransfer.dropEffect = drop.getAttribute("data-drop-effect");
drop.classList.add("drop-active");
}
function dragOver(event){
var drop = this;
event.dataTransfer.dropEffect = drop.getAttribute("data-drop-effect");
var isPreventDefault = (event.dataTransfer.effectAllowed == "all" || event.dataTransfer.effectAllowed.toLowerCase().indexOf(event.dataTransfer.dropEffect) > -1);
if (isPreventDefault) {
event.preventDefault();
drop.classList.add("drop-active");
}
}
function dragLeave(event){
var drop = this;
drop.classList.remove("drop-active");
}
function drag(event){}
function dragEnd(event){
var drag = this;
drag.classList.remove("drag-active");
}
function drop(event){
event.preventDefault();
var drop = this;
drop.classList.remove("drop-active");
drop.classList.add("correct");
event.dataTransfer.dropEffect = drop.getAttribute("data-drop-effect");
var dataList, dataHTML, dataText;
try{
dataList = event.dataTransfer.getData('text/uri-list');
dataHTML = event.dataTransfer.getData('text/html');
}catch(e){
dataText = event.dataTransfer.getData('text');
}
if (dataHTML) {
drop.innerHTML = "";
drop.insertAdjacentHTML("afterbegin", dataHTML);
}else{
drop.innerHTML = "";
var dropClone = dragItem.cloneNode(true);
drop.insertAdjacentElement("afterbegin", dropClone);
}
}
var resetBtn = document.querySelector(".reset-button");
resetBtn.onclick = function(event){
var images = document.querySelectorAll(".drop-zone img");
images.forEach(function(img){
img.parentNode.classList.remove("correct");
img.remove();
});
}
}
</script>
示例:拼图小游戏
<style>
.cf:before,
.cf:after {content: " ";display: table;}
.cf:after {clear: both;}
.image-drag .container {
max-width: 690px; float: left; margin: 0 10px 20px 10px;
}
.image-drag .gallery-list {
width: 320px; min-height: 317px; background: #eee;
border: solid 1px #ccc; padding: 5px 0 5px 5px;
}
.image-drag .gallery-list .drag {
width: 150px; display: inline-block;
height: 150px; border: solid 1px #ccc;
cursor: grab; border: solid 1px transparent;
}
.image-drag .gallery-list .drag.drag-active {
border: solid 1px #2c3e50;
}
.image-drag .gallery-painting {
width: 320px; background: #eee;
background-position: center center;
background-size: cover; border: solid 1px #ccc;
padding: 5px 0 5px 5px;
}
.image-drag .gallery-painting .drop {
position: relative; height: 150px; width: 150px;
display: inline-block; background: rgba(50, 50, 50, 0.7);
border: solid 1px transparent; overflow: hidden;
}
.image-drag .gallery-painting .drop.drop-active {
border: solid 1px #f1c40f;
}
.image-drag .gallery-painting .drop.correct {
border: solid 1px #32ce74;
}
.image-drag .gallery-painting .drop.incorrect {
border: solid 1px #c0392b;
}
.image-drag .gallery-painting .drop img {
max-width: 100%; height: auto;
}
.reset-button {
background: #eee; border: solid 1px #ccc;
padding: 12px 15px; display: block;
cursor: pointer; margin: 0 auto;
min-width: 100px; clear: both;
}
.message{ clear: both;}
</style>
<div class="image-drag cf">
<div class="container">
<h3>拖动下面的拼图块</h3>
<div class="gallery-list cf">
<img draggable="true" src="images/d2.jpg" class="drag" data-value="2" />
<img draggable="true" src="images/d3.jpg" class="drag" data-value="3" />
<img draggable="true" src="images/d1.jpg" class="drag" data-value="1" />
<img draggable="true" src="images/d4.jpg" class="drag" data-value="4" />
</div>
</div>
<div class="container">
<h3>把拼图放在这里</h3>
<div class="gallery-painting cf">
<div class="drop" data-value="1"></div>
<div class="drop" data-value="2"></div>
<div class="drop" data-value="3"></div>
<div class="drop" data-value="4"></div>
</div>
</div>
<div class="message"></div>
<button class="reset-button">Reset Puzzle</button>
</div>
<script>
window.onload = function(){
var drag_items = document.querySelectorAll(".image-drag .drag");
var drop_items = document.querySelectorAll(".image-drag .drop");
function setUpEventListeners(){
drag_items.forEach(function(item){
item.addEventListener('dragstart', dragStart);
item.addEventListener('drag', drag);
item.addEventListener('dragend', dragEnd);
});
drop_items.forEach(function(item){
item.addEventListener('dragenter', dragEnter);
item.addEventListener('dragover', dragOver);
item.addEventListener('dragleave', dragLeave);
item.addEventListener('drop', drop);
});
}
setUpEventListeners();
var dragItem;
function dragStart(event){
var drag = event.target;
dragItem = event.target;
event.dataTransfer.effectAlllowed = "copy";
var imageSrc = dragItem.src;
var imageHTML = dragItem.outerHTML;
console.log(imageHTML);
try {
event.dataTransfer.setData('text/uri-list', imageSrc);
event.dataTransfer.setData('text/html', imageHTML);
} catch (e) {
event.dataTransfer.setData('text', imageSrc);
}
drag.classList.add("drag-active");
}
function dragEnter(event){
event.preventDefault();
var drop = this;
event.dataTransfer.dropEffect = "copy";
drop.classList.add("drop-active");
}
function dragOver(event){
var drop = this;
event.dataTransfer.dropEffect = "copy";
event.preventDefault();
drop.classList.add("drop-active");
}
function dragLeave(event){
var drop = this;
drop.classList.remove("drop-active");
}
function drag(event){}
function dragEnd(event){
var drop = this;
drop.classList.remove("drop-active");
}
function drop(event){
event.preventDefault();
var drop = this;
drop.classList.remove("drop-active");
var dataList, dataHTML, dataText;
try {
dataList = event.dataTransfer.getData('text/uri-list');
dataHTML = event.dataTransfer.getData('text/html');
} catch (e) {;
dataText = event.dataTransfer.getData('text');
}
if (dataHTML) {
drop.innerHTML = "";
drop.insertAdjacentHTML("afterbegin", dataHTML);
var drag = drop.querySelector(".drag");
}else{
drop.innerHTML = "";
var dropClone = dragItem.cloneNode(true);
drop.insertADjacentElement("afterbegin", dropClone);
var drag = drop.querySelector(".drag");
}
checkCorrectDrop(drop, drag);
checkCorrectFinalImage();
}
function checkCorrectDrop(drop, drag){
var imageValue = drag.getAttribute("data-value");
var dropValue = drop.getAttribute("data-value");
if (imageValue == dropValue) {
drop.classList.remove("incorrect");
drop.classList.add("correct");
drag.draggable = false;
dragItem.style.display = "none";
}else{
drop.classList.remove("correct");
drop.classList.add("incorrect");
}
}
function checkCorrectFinalImage(){
var correctItems = [];
drop_items.forEach(function(item){
if (item.classList.contains("correct")) {
correctItems.push(item);
}
});
var message = document.querySelector(".message");
if (correctItems.length == drop_items.length) {
message.innerHTML = "";
message.insertAdjacentHTML("beforeend", "<h3>你完成了拼图</h3>");
}else{
message.innerHTML = "";
}
}
var resetBtn = document.querySelector(".reset-button");
resetBtn.onclick = function(){
drop_items.forEach(function(item){
if (item.firstChild) {
item.removeChild(item.firstChild);
item.classList.remove("correct,incorrect");
}
});
drag_items.forEach(function(item){
item.style.display = "inline-block";
})
var message = document.querySelector(".message");
message.innerHTML = "";
}
}
</script>
files属性:
包含数据传输中可用的所有本地文件的列表信息,常用于将文件从系统拖向浏览器;如果拖动操作不涉及拖动文件,则此属性为空列表;
container.addEventListener("drop", function(event){
event.preventDefault();
console.log(event.dataTransfer.files); // FileList
console.log(event.dataTransfer.types); // ["Files"]
}, false);
从本地拖入一个或多个文件到container中,打印的files属性为FileList类型的列表对象;该列表保存的是表示文件的File对象;
container.addEventListener("drop", function(event){
event.preventDefault();
var dt = event.dataTransfer;
var files = dt.files;
var count = files.length;
console.log("文件数量:" + count);
for(var i=0; i<files.length; i++){
console.log("File " + i + ":" + (typeof files[i]) + ":<" + files[i] + ">" +
files[i].name + " " + files[i].size);
}
}, false);
示例:完善垃圾桶效果
<p id="fileinfo" style="clear: both;">文件数量:未知</p>
<script type="text/javascript">
window.onload = function(){
var container = document.getElementById("container");
var fileinfo = document.getElementById("fileinfo");
var filecount = 0;
function init(){
var oDiv = container.getElementsByTagName("div");
filecount = oDiv.length;
fileinfo.textContent = "文件数量:" + filecount;
var recycle = document.getElementById("recycle");
for (var i = 0; i < oDiv.length; i++) {
oDiv[i].ondragstart = function(event) {
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text", event.target.id);
console.log("开始拖动" + event.target.id);
}
}
recycle.ondragenter = function(event){
event.preventDefault();
event.dataTransfer.dropEffect = "move";
}
recycle.ondragover = function(event) {
event.preventDefault();
console.log("可以放置到垃圾桶");
}
recycle.ondrop = function(ev) {
event.preventDefault();
var id = event.dataTransfer.getData("text");
var folder = document.getElementById(id);
document.getElementById("container").removeChild(folder);
// oDiv.parentNode.removeChild(oDiv);
filecount--;
fileinfo.textContent = "文件数量:" + filecount;
}
}
init();
container.addEventListener("dragover", function(event){
event.preventDefault();
})
container.addEventListener("drop", function(event){
event.preventDefault();
var dt = event.dataTransfer;
var files = dt.files;
var count = files.length;
var maxid = container.getElementsByTagName("div")[container.getElementsByTagName("div").length-1].id;
maxid = Number(maxid.match(/\d+/)[0]) + 1;
var file = null, div = null;
for(var i=0; i < count; i++){
file = files[i];
div = document.createElement("div");
div.draggable = true;
div.id = "file" + (maxid + i);
div.innerHTML = file.name + "<br/>" + file.size;
event. currentTarget.appendChild(div);
}
init();
}, false);
}
</script>
示例:拖拽图片和记事本文件进行预览
<style type="text/css">
div#show{
border: 1px solid #ccc;
width: 400px; height: 300px; display: -moz-box; display: -webkit-box;
-moz-box-align: center; -webkit-box-align: center;
-moz-box-pack: center; -webkit-box-pack: center;
resize:both; overflow:auto;
}
div[id^="show"]:hover{ border: 1px solid rgb(248, 194, 16) !important; }
</style>
<div id="show">文件预览区,仅限图片和txt文件</div>
<script type="text/javascript">
function init() {
var dest = document.getElementById("show");
dest.addEventListener("dragover", function(event) {
event.preventDefault();
}, false);
dest.addEventListener("dragend", function(event) {
event.preventDefault();
}, false);
dest.addEventListener("drop", function (event) {
event.preventDefault();
console.log(event.dataTransfer)
var file = event.dataTransfer.files[0];
var reader = new FileReader();
if (file.type.substr(0, 5) == "image") {
reader.onload = function (e) {
dest.style.background = 'url(' + e.target.result + ') no-repeat center';
dest.innerHTML = "";
};
reader.readAsDataURL(file);
}else if (file.type.substr(0, 4) == "text") {
reader.readAsText(file);
reader.onload = function (e) {
dest.innerHTML = "<pre>" + this.result + "</pre>";
dest.style.background = "white";
}
}else {
dest.innerHTML = "暂不支持此类文件的预览";
dest.style.background = "white";
}
}, false);
}
document.ondragover = function(e){e.preventDefault();};
document.ondrop = function(e){e.preventDefault();}
window.onload=init;
</script>
示例:拖入多个图片
<style>
.container {max-width: 700px; margin: 0px auto;}
.container .drop {
min-height: 300px; background: #eee; border: solid 2px #ddd;
padding: 15px; position: relative; margin-bottom: 20px;
transition: all 300ms ease-in;
}
.container .drop.drop-active {
border: dashed 2px #fc0; background: #ccc;
}
.container .drop:before {
content: '\f019'; position: absolute; z-index: 1;
top: 50%; left: 50%; top: calc(50% - 40px); left: calc(50% - 40px);
width: 80px; height: 80px; font-size: 80px; line-height: 80px;
color: #ccc; transition: all 300ms ease-in;
}
.container .drop.drop-active:before { color: #aaa;}
.container .drop img {
max-width: 100%; height: auto; position: relative; z-index: 2;
}
.container .message {margin-bottom: 20px; word-break: break-all;}
.container .message.active {border: solid 1px #ccc; padding: 15px;}
</style>
<div class="container">
<h3>在下方区域拖入image图片</h3>
<div class="drop"></div>
<div class="drop"></div>
<div class="message"></div>
</div>
<script>
window.onload = function(){
var drop_items = document.querySelectorAll(".drop");
// 初始化事件
function setUpEventListeners() {
drop_items.forEach(function(item,key){
item.addEventListener('dragenter', dragEnter);
item.addEventListener('dragover', dragOver);
item.addEventListener('dragleave', dragLeave);
item.addEventListener('drop', drop);
});
}
setUpEventListeners();
function dragEnter(event){
event.preventDefault();
event.target.classList.add("drop-active");
}
function dragOver(event){
event.preventDefault();
event.target.classList.add("drop-active");
}
function dragLeave(event){
event.target.classList.remove("drop-active");
}
function drop(event){
event.preventDefault();
drop = this;
drop.classList.remove("drop-active");
var dataValue, dataType, imageDropped, dataList, dataText;
try {
dataType = "text/uri-list"
dataValue = event.dataTransfer.getData(dataType);
} catch (e) {
dataType = "URL";
dataValue = event.dataTransfer.getData(dataType);
}
console.log(dataType, dataValue);
if (dataValue) {
var message = document.querySelector('.message');
message.textContent = "";
message.classList.add('active');
message.insertAdjacentHTML("beforeend",'<p>从外部窗口中拖入的元素<p>');
drop.innerHTML = "";
if (dataType == 'text/uri-list') {
message.insertAdjacentHTML("beforeend",'<ul><li><strong>Text/URI-List: </strong>' + dataValue + '</li></ul>');
}
if (dataType == 'URL') {
message.insertAdjacentHTML("beforeend",'您使用的是IE,它只支持 "text" or "URL" 类型');
message.insertAdjacentHTML("beforeend",'<ul><li><strong>URL: </strong>' + dataValue + '</li></ul>');
}
imageDropped = false;
var imageExtensions = ['.jpg', '.jpeg', '.png', '.bmp', '.gif'];
for (i = 0; i < imageExtensions.length; i++) {
if (dataValue.indexOf(imageExtensions[i]) !== -1) {
var image = '<img src="' + dataValue + '">';
drop.insertAdjacentHTML("beforeend",image);
imageDropped = true;
break;
}
}
if (imageDropped == true) {
message.insertAdjacentHTML("beforeend",'<p><strong>成功拖入了一个在线图片</strong></p>');
} else {
message.insertAdjacentHTML("beforeend",'<p><strong>无法确定拖入的内容,它与预期的图像格式不匹配</strong></p>');
}
}else{
var dataFiles = event.dataTransfer.files;
var dataOutput = [];
if (dataFiles) {
var message = document.querySelector('.message');
message.innerHTML = "";
message.classList.add('active');
message.insertAdjacentHTML("beforeend",'<p>从桌面上拖入了一个或多个项目:<p>');
drop.innerHTML = "";
for (i = 0; i < dataFiles.length; i++) {
var dataItem = dataFiles[i];
var dataType = dataFiles[i].type;
if (dataType.match('image.*')) {
var dataLastModified = dataFiles[i].lastModified;
var dataLastModifiedDate = dataFiles[i].lastModifiedDate;
var dataName = dataFiles[i].name;
var dataSize = dataFiles[i].size;
var dataType = dataFiles[i].type;
message.insertAdjacentHTML("beforeend",'<ul>');
message.insertAdjacentHTML("beforeend",'<li><strong>名称</strong>:' + dataName + '</li>');
message.insertAdjacentHTML("beforeend",'<li><strong>大小</strong>:' + dataSize + '</li>');
message.insertAdjacentHTML("beforeend",'<li><strong>类型</strong>:' + dataType + '</li>');
message.insertAdjacentHTML("beforeend",'</ul>');
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var url = e.target.result;
drop.insertAdjacentHTML("beforeend",'<img src="' + url + '" title="' + dataName + '"/>');
message.insertAdjacentHTML("beforeend",'<p><strong>从桌面上成功的拖入一个图片</strong></p>');
};
})(dataItem);
reader.readAsDataURL(dataItem);
}else{
message.insertAdjacentHTML("beforeend",'<p><strong>您拖入的不是图像,只允许使用图像</strong></p>');
}
}
}
}
}
}
</script>
items属性:
返回一个包含所有拖动数据列表的只读的DataTransferItemList对象,其中的数据项为DataTransferItem对象,其代表一个拖动项;如果拖动操作中没有数据,则该列表为空;
p.addEventListener("dragstart", function(event){
event.dataTransfer.setData("text/plain", event.target.id);
event.dataTransfer.setData("text/uri-list", "https://www.zeronetwork.cn/");
},false);
container.addEventListener("drop", function(event){
console.log(event.dataTransfer.items); // DataTransferItemList
}, false);
IE不支持items属性;
DataTransferItem对象描述了一个拖动数据项;该对象拥有两个属性:
- kind属性:只读,返回拖动数据的种类,string或是file;
- type:只读,返回拖动数据的MIME类型,如“text/plain“、”text/uri-list”、”text/html”等,具体到file种类的话,有”image/png”、”image/jpeg”、”image/png”等类型;
container.addEventListener("drop", function(event){
console.log(event.dataTransfer.items); // DataTransferItemList
if(event.dataTransfer.items != null){
for(var i=0; i<event.dataTransfer.items.length; i++){
console.log(event.dataTransfer.items[i]); // DataTransferItem
console.log("> items[" + i + "] = " + event.dataTransfer.items[i].kind + "; type = " + event.dataTransfer.items[i].type);
}
}
}, false);
DataTransferItemList集合对象属性和方法:
- length属性:返回拖动项的数量;
- add(data, type)方法:向拖动项列表中添加新项(File或string),该方法会返回一个DataTransferItem对象;注意,已存在类型不能再添加,否则会抛出异常;
- remove(index):根据索引从拖动项列表中删除拖动项;
- clear():清空拖动项列表;
img.addEventListener("dragstart", function(event){
event.dataTransfer.clearData();
console.log(event.dataTransfer.items);
var dataList = event.dataTransfer.items;
dataList.add(event.target.id, "text/plain");
var item = dataList.add("<p>大师哥<strong>王唯</strong></p>", "text/html");
console.log(item); // DataTransferItem
dataList.add("https://www.zeronetwork.cn/", "text/uri-list");
dataList.add("http://127.0.0.1:5500/images/1.jpg", "image/jpeg");
},false);
remove()和clear()方法只能用在dragstart事件中;
img.addEventListener("dragstart", function(event){
// event.dataTransfer.clearData();
for(var i=event.dataTransfer.items.length - 1; i>=0; i--){
event.dataTransfer.items.remove(i);
}
// 或者
event.dataTransfer.items.clear();
//...
},false);
DataTransferItem对象方法:
getAsString(callback):
参数callback是一个回调函数,当DataTransferItem对象的kind属性是一个普通Unicode字符串时,该方法会用 DataTransferItem对象的数据作为参数来执行传入的回调函数;如:
container.addEventListener("drop", function(event){
event.preventDefault();
var item = event.dataTransfer.items[0];
item.getAsString(function(s){
console.log(s); // myp
});
console.log(event.dataTransfer.getData("text")); // myp
}, false);
如:
<style>
div {margin: 20px auto; padding: 20px;}
#src_copy, #src_move {
color: blue; border: 5px solid black;
width: 300px; height: 50px;
}
#dest_copy, #dest_move {
border: 5px solid blue; width: 300px; height: 50px;
}
</style>
<script>
function dragstart_handler(event) {
var dti = event.dataTransfer.items;
if (dti === undefined || dti == null) {
console.log("不支持 DataTransferItem");
return;
}
event.currentTarget.style.border = "dashed";
dti.add(event.target.id, "text/plain");
event.effectAllowed = "copyMove";
}
function dragover_handler(event) {
event.preventDefault();
var dti = event.dataTransfer.items;
if (dti === undefined || dti == null) {
console.log("不支持 DataTransferItem");
return;
}
event.currentTarget.style.background = "lightblue";
}
function drop_handler(event) {
event.preventDefault();
var dti = event.dataTransfer.items;
if (dti === undefined || dti == null) {
console.log("不支持 DataTransferItem");
return;
}
for (var i=0; i < dti.length; i++) {
console.log("Drop: item[" + i + "].kind = " + dti[i].kind + " ; item[" + i + "].type = " + dti[i].type);
if ((dti[i].kind == 'string') && (dti[i].type.match('^text/plain'))) {
dti[i].getAsString(function (id){
if (id == "src_move" && event.target.id == "dest_move")
event.target.appendChild(document.getElementById(id));
if (id == "src_copy" && event.target.id == "dest_copy") {
var nodeCopy = document.getElementById(id).cloneNode(true);
nodeCopy.id = "newId";
event.target.appendChild(nodeCopy);
}
});
}
}
}
function dragend_handler(event) {
var dti = event.dataTransfer.items;
if (dti === undefined || dti == null) {
console.log("不支持 DataTransferItem");
return;
}
event.target.style.border = "solid black";
}
</script>
<div draggable="true" id="src_copy" ondragstart="dragstart_handler(event);" ondragend="dragend_handler(event);">
把此元素拖放到copy区
</div>
<div id="dest_copy" ondrop="drop_handler(event);" ondragover="dragover_handler(event);">
Copy区
</div>
<div draggable="true" id="src_move" ondragstart="dragstart_handler(event);">
把此元素拖放到move区
</div>
<div id="dest_move" ondrop="drop_handler(event);" ondragover="dragover_handler(event);">
Move区
</div>
getAsString()方法与DataTransfer.getData()方法作用基本相同,区别是getData()是同步的,而getAsString()是异步的,并且getAsString()支持回调函数;
getAsFile():
如果DataTransferItem是一个文件, 该方法将返回拖动项数据的File对象. 如果拖动项的数据不是一个文件,则返回null;
container.addEventListener("drop", function(event){
event.preventDefault();
var item = event.dataTransfer.items[0];
var file = item.getAsFile();
console.log(file); // File
}, false);
示例:
container.addEventListener("drop", function(event){
event.preventDefault();
var data = event.dataTransfer.items;
for(var i=0; i<data.length; i++){
if((data[i].kind == "string") && (data[i].type.match("^text/plain"))){
data[i].getAsString(function(s){
event.target.appendChild(document.getElementById(s));
});
}else if((data[i].kind == "string") && (data[i].type.match("^text/html"))){
data[i].getAsString(function(s){
console.log("HTML:" + s);
});
}else if((data[i].kind == "string") && (data[i].type.match("^text/uri-list"))){
data[i].getAsString(function(s){
console.log("URI:" + s);
})
}else if((data[i].kind == "file") && (data[i].type.match("^image/"))){
var f = data[i].getAsFile();
console.log("拖动文件");
}
}
}, false);
设置反馈:
setDragImage(element, x, y)设置拖动反馈图像:
当拖动发生时,会生成拖动目标的一个半透明的图像副本,并在拖动过程中跟随鼠标移动;这个图像是自动创建的,称为拖动反馈图像,但也可以自定义这个拖动反馈图像;
该方法指定一个图像,当拖动发生时,提示在光标下方;其接收三个参数,分别是要显示的图像元素和光标在图像中的x、y坐标(即图像相对于鼠标指针位置的偏移量);其中,element可以是一个img,也可以是canvas或其他任何元素;
IE不支持此方法;
注意:图像会以原始的大小呈现,另外,其也可以不在文档树中,如果是其他元素,Chrome需要其位于文档树中;
var dragImg = new Image(100,100); // 该参数无用,还会使用图像原始尺寸
dragImg.src = "images/5.jpg";
p.addEventListener("dragstart", function(event){
var dt = event.dataTransfer;
dt.setData("text/plain","拖动图像了");
dt.setDragImage(dragImg,100,100);
},false);
这种技术在使用canvas元素绘制自定义的拖动反馈图像时非常有用,如:
img.addEventListener("dragstart", function(event){
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
var ctx = canvas.getContext("2d");
ctx.lineWidth = 4;
ctx.moveTo(0, 0);
ctx.lineTo(100,100);
ctx.moveTo(0,100);
ctx.lineTo(100,0);
ctx.stroke();
var dt = event.dataTransfer;
dt.setData("text/plain","拖动图像了");
dt.setDragImage(canvas,50,50);
},false);
Chrome需要把canvas添加到文档树中才可以,如:
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 100;
canvas.style.cssText = "position:absolute;zIndex=-1;top:-100px;left:-100px;display='none'";
var ctx = canvas.getContext("2d");
ctx.lineWidth = 4;
ctx.moveTo(0, 0);
ctx.lineTo(100,100);
ctx.moveTo(0,100);
ctx.lineTo(100,0);
ctx.stroke();
document.body.appendChild(canvas);
img.addEventListener("dragstart", function(event){
var dt = event.dataTransfer;
dt.setData("text/plain","拖动图像了");
canvas.style.display = "block";
dt.setDragImage(canvas,50,50);
},false);
img.addEventListener("dragend", function(event){
canvas.style.display = "none";
});
放置反馈:
鼠标指针会根据dropEffect属性的值做必要的更新,其具体的外观取决于用户平台,例如,典型的是如果有“+”图标表示'copy',而不允许放置时,会出现禁止放置的图标;在大多数情况下,使用系统默认的鼠标指针的反馈就足够了;
但也可以根据需要自定义,如添加一个插入标记或使用高亮显示;对于简单的高亮显示,可以在放置目标上使用 -moz-drag-over CSS 伪类,如:
<style>
#container:-moz-drag-over{border: 5px dashed black !important;}
</style>
必须在放置元素的dragenter事件中调用 preventDefault() 方法时才有效;但只有Firefox支持;
对于更复杂的视觉效果或者不支持-moz-drag-over伪类的浏览器,可以在dragenter事件中执行某些操作;例如在放置位置插入一个元素,这样的元素可以表示一个插入标记,或表示被拖拽的元素移动到了新位置,或者为放置元素设置新的视觉效果;
<style>
.container_over {border: 5px dashed black;}
</style>
<script>
mydiv.addEventListener("dragenter", function(event){
event.preventDefault();
event.target.classList.add("container_over");
},false);
</script>
甚至可以在dragover事件中,利用事件的clientX和clientY属性来定位插入的新元素,即跟随鼠标位置;
最后,在dragleave事件中移除插入标记或高亮效果;但使用-moz-drag-over伪类设置的高亮或其他视觉效果会被自动移除,不需要在此手动移除;
container.addEventListener("dragleave", function(event){
event.target.classList.remove("container_over");
},false);
示例:停车场
<style>
*{margin:0; padding: 0;}
body{background-color: #e2e8f3;}
ul,li{list-style: none;}
img{cursor: pointer;}
button{background-color: lightgray;border: 1px solid grey;}
button:hover{background-color: #ffd92c;}
.container{height: 100%; display: flex;justify-content: space-around;align-items: center;}
.parking-rules{padding: 0;}
.parking-rules > li{display:flex; justify-content: center; text-align: center; background-color: #fdfefe; padding: 16px; margin: 12px;}
.parking-rules > li:nth-child(n){border:2px solid #538cff;}
.parking-rules > li:nth-child(2n){border:2px solid #d51c00;}
.parking-rules > li:nth-child(3n){border:2px solid #ffd92e;}
.parking-rules > li h2{width: 60%;}
.drop-zone{position: relative; width: 300px; height: 300px;overflow: hidden;background-color: #878787;color: white;box-shadow: 0 19px 38px rgba(0,0,0,0.3), 0 15px 12px rgba(0,0,0,0.22);}
.drop-zone{display: flex; align-items: center; justify-content: center;}
.drop-zone .dashing{display: block; width: 100%; height: 100%; position: absolute;}
.drop-zone .dashing:nth-of-type(1){transform: rotate(0deg);}
.drop-zone .dashing:nth-of-type(2){transform: rotate(90deg);}
.drop-zone .dashing:nth-of-type(3){transform: rotate(180deg);}
.drop-zone .dashing:nth-of-type(4){transform: rotate(270deg);}
.drop-zone .dashing i{display: block; position: absolute; left: 0; top: 0;width: 200%; border-bottom: 12px dashed;animation: slideDash 5.5s infinite linear;}
@keyframes slideDash{
from{transform: translateX(-50%);}
to{transform: translateX(0%);}
}
.drag-vehicle h2::before{content: "\2190";}
.vehicles img{visibility: visible; opacity: 1;}
.vehicles img.hide{visibility: hidden; opacity: 0;}
/* p{color:#1f904e} */
</style>
<div class="container">
<ul class="parking-rules">
<li>
<h2>仅限救护车停车</h2>
<p>晚上九时至凌晨三时<br/>周一至周五</p>
</li>
<li>
<h2>仅限消防车停车</h2>
<p>24小时<br/>星期六至星期日</p>
</li>
<li>
<h2>普通停车场</h2>
<p>凌晨三时至下午三时<br/>周一至周五</p>
</li>
<li>
<h2>自行车停车场</h2>
<p>下午三时至九时<br/>周一至周五</p>
</li>
</ul>
<section class="drop-zone">
<span class="dashing"><i></i></span>
<span class="dashing"><i></i></span>
<span class="dashing"><i></i></span>
<span class="dashing"><i></i></span>
</section>
<section class="drag-vehicle">
<h2>停车</h2>
<ul class="vehicles">
<li><img draggable="true" alt="fire" src="images/fire.png" /></li>
<li><img draggable="true" alt="ambulance" src="images/ambulance.png" /></li>
<li><img draggable="true" alt="car" src="images/car.png" /></li>
<li><img draggable="true" alt="bicycle" src="images/bicycle.png" /></li>
</ul>
<button class="filter-vehicles">显示可以停车的车辆</button>
<button class="show-all">显示所有</button>
</section>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.29.1/moment.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/moment-range/4.0.2/moment-range.js"></script>
<script>
window['moment-range'].extendMoment(moment);
var parking = (function () {
var weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
var parking = {
parkingRules: {
ambulance: {
days: weekdays,
times: createRange(moment().set('hour', 21), moment().add(1, 'day').set('hour', 3))
},
'fire truck': {
days: ['Saturday', 'Sunday']
},
car: {
days: weekdays,
times: createRange(moment().set('hour', 3), moment().set('hour', 15))
},
bicycle: {
days: weekdays,
times: createRange(moment().set('hour', 15), moment().set('hour', 21))
}
},
dropZone: document.querySelector('.drop-zone'),
vehicleArray: Array.prototype.slice.call(document.querySelectorAll('.vehicles img')),
dragged: null, // 拖动的车辆
areVehiclesFiltered: false
}
return Object.seal(parking);
})();
function addEventListeners() {
var filterButton = document.querySelector('.drag-vehicle .filter-vehicles');
var showButton = document.querySelector('.drag-vehicle .show-all');
var vehicles = document.querySelector('.vehicles');
var dropZone = parking.dropZone;
vehicles.addEventListener('dragstart', onDragStart);
vehicles.addEventListener('dragend', onDragEnd);
dropZone.addEventListener('dragenter', onDragEnter);
dropZone.addEventListener('dragover', onDragOver);
dropZone.addEventListener('dragleave', onDragLeave);
dropZone.addEventListener('drop', onDrop);
filterButton.addEventListener('click', showVehiclesThatCanPark);
showButton.addEventListener('click', resetVehicles);
}
function createRange(start, end) {
if (start && end) {
return moment.range(start, end);
}
}
function onDragStart(event) {
var target = event.target;
if (target && target.nodeName == 'IMG') {
var imgSrc = target.src;
parking.dragged = target;
event.dataTransfer.effectAllowed = 'linkMove';
event.dataTransfer.setData('text/uri-list', imgSrc);
event.target.style.opacity = .5;
}
}
function onDragEnd(event) {
if (event.target && event.target.nodeName == 'IMG') {
event.target.style.opacity = 1;
}
}
function onDragEnter(event) {
var target = validateTarget(event.target);
var dragged = parking.dragged;
if (dragged && target) {
var isLink = contains(event.dataTransfer.types, 'text/uri-list');
var vehicleType = dragged.alt;
if (isLink && canPark(vehicleType)) {
event.preventDefault();
event.dataTransfer.dropEffect = 'linkMove'
target.style.background = '#1f904e'; // 绿色
}else {
target.style.backgroundColor = '#d51c00'; // 红色
}
}
}
function onDragOver(event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'linkMove'
}
function onDragLeave(event) {
var target = validateTarget(event.target);
target.style.background = '';
}
function onDrop(event) {
event.preventDefault();
var target = validateTarget(event.target);
var dragged = parking.dragged;
if (dragged && target) {
var isLink = contains(event.dataTransfer.types, 'text/uri-list');
var vehicleType = dragged.alt;
target.style.backgroundColor = '';
if (isLink && canPark(vehicleType)) {
// dragged.parentNode.removeChild(dragged); // 不用
dragged.style.opacity = 1;
target.appendChild(dragged);
}
}
}
function validateTarget(target) {
var dropZone = parking.dropZone;
return (target.parentNode === dropZone || target === dropZone) ? dropZone : null;
}
function contains(list, value) {
for (var i = 0; i < list.length; ++i) {
if (list[i] === value) return true;
}
return false;
}
function getDay() {
return moment().format('dddd');
}
function getHours() {
return moment().hour();
}
function canPark(vehicle) {
var parkingRules = parking.parkingRules;
if (vehicle && parkingRules[vehicle]) {
var rules = parkingRules[vehicle];
var validDays = rules.days;
var validTimes = rules.times;
var curDay = getDay();
if (validDays) {
return validDays.includes(curDay) && (validTimes ? validTimes.contains(moment()) : true);
}
}
return false;
}
function showVehiclesThatCanPark() {
parking.vehicleArray.filter(function(vehicle){
return !canPark(vehicle.alt);
}).forEach(function(vehicle){
vehicle.classList.add("hide");
});
parking.areVehiclesFiltered = true;
}
function resetVehicles() {
if (parking.areVehiclesFiltered) {
parking.vehicleArray.forEach(function(vehicle){
vehicle.classList.remove("hide");
});
parking.areVehiclesFiltered = false;
}
}
addEventListeners();
</script>