一、介紹
mousedown mouseup mousemove
dragstart dragend drag (在拖動(dòng)元素上執(zhí)行) 首先要在此元素上設(shè)置draggable="true" 為可拖
dragenter dragleave? dragover (在目標(biāo)元素上執(zhí)行)
drop 放下 (在目標(biāo)元素上執(zhí)行)
dropzone表示可以放到哪個(gè)區(qū)域
test
上面三個(gè)都有類似之處
transfer 轉(zhuǎn)移
二藻丢、代碼
CSS樣式:
#box-wrap {
? ? width: 600px;
? ? height: 450px;
? ? margin-top: 20px;
? ? border: 1px solid green;
}
#box {
? ? width: 400px;
? ? height: 260px;
? ? margin: 20px 0 0 20px;
? ? border: 1px solid red;
? ? outline: 1px solid blue;
? ? outline-offset: 10px;
? ? background: -webkit-linear-gradient(left, red, orange, yellow, green, blue, indigo, violet);
? ? background: -moz-linear-gradient(left, red, orange, yellow, green, blue, indigo, violet);
? ? background: -o-linear-gradient(left, red, orange, yellow, green, blue, indigo, violet);
? ? background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet);
}
HTML布局:
JS腳本:
// 拖動(dòng)元素
var eleBox = document.getElementById('box');
eleBox.addEventListener('dragstart', function(e) {
? ? e.dataTransfer.setData('ele', e.target.id);? // 獲取拖動(dòng)元素的id
});
eleBox.addEventListener('drag', function(e) {
? ? console.log('持續(xù)拖動(dòng)胡岔!');
});
eleBox.addEventListener('dragend', function(e) {
? ? console.log('結(jié)束拖動(dòng)!');
});
// 目錄元素
var eleBoxWrap = document.getElementById('box-wrap');
eleBoxWrap.addEventListener('dragenter', function(e) {
? ? console.log('拖動(dòng)元素已進(jìn)入此容器歹袁!');
});
eleBoxWrap.addEventListener('dragover', function(e) {
? ? console.log('拖動(dòng)元素懸浮在此容器上坷衍!');
? ? e.preventDefault();
});
eleBoxWrap.addEventListener('dragleave', function(e) {
? ? console.log('拖動(dòng)元素已離開此容器!');
});
eleBoxWrap.addEventListener('drop', function(e) {
? ? e.preventDefault();
? ? var data = e.dataTransfer.getData('ele');? // 獲取拖動(dòng)元素的id
? ? e.target.appendChild(document.getElementById(data));
? ? console.log('放下')
});
詳細(xì)介紹(源自http://javascript.ruanyifeng.com/dom/event.html)
當(dāng)Element節(jié)點(diǎn)或選中的文本被拖拉時(shí)条舔,就會(huì)持續(xù)觸發(fā)拖拉事件枫耳,包括以下一些事件。
drag事件:拖拉過程中孟抗,在被拖拉的節(jié)點(diǎn)上持續(xù)觸發(fā)迁杨。
dragstart事件:拖拉開始時(shí)在被拖拉的節(jié)點(diǎn)上觸發(fā)钻心,該事件的target屬性是被拖拉的節(jié)點(diǎn)。通常應(yīng)該在這個(gè)事件的監(jiān)聽函數(shù)中仑最,指定拖拉的數(shù)據(jù)扔役。
dragend事件:拖拉結(jié)束時(shí)(釋放鼠標(biāo)鍵或按下escape鍵)在被拖拉的節(jié)點(diǎn)上觸發(fā),該事件的target屬性是被拖拉的節(jié)點(diǎn)警医。它與dragStart事件亿胸,在同一個(gè)節(jié)點(diǎn)上觸發(fā)。不管拖拉是否跨窗口预皇,或者中途被取消侈玄,dragend事件總是會(huì)觸發(fā)的柱查。
dragenter事件:拖拉進(jìn)入當(dāng)前節(jié)點(diǎn)時(shí)梯码,在當(dāng)前節(jié)點(diǎn)上觸發(fā)唱蒸,該事件的target屬性是當(dāng)前節(jié)點(diǎn)偎痛。通常應(yīng)該在這個(gè)事件的監(jiān)聽函數(shù)中,指定是否允許在當(dāng)前節(jié)點(diǎn)放下(drop)拖拉的數(shù)據(jù)航背。如果當(dāng)前節(jié)點(diǎn)沒有該事件的監(jiān)聽函數(shù)庞萍,或者監(jiān)聽函數(shù)不執(zhí)行任何操作嗤栓,就意味著不允許在當(dāng)前節(jié)點(diǎn)放下數(shù)據(jù)爬橡。在視覺上顯示拖拉進(jìn)入當(dāng)前節(jié)點(diǎn)治唤,也是在這個(gè)事件的監(jiān)聽函數(shù)中設(shè)置。
dragover事件:拖拉到當(dāng)前節(jié)點(diǎn)上方時(shí)糙申,在當(dāng)前節(jié)點(diǎn)上持續(xù)觸發(fā)宾添,該事件的target屬性是當(dāng)前節(jié)點(diǎn)。該事件與dragenter事件基本類似柜裸,默認(rèn)會(huì)重置當(dāng)前的拖拉事件的效果(DataTransfer對(duì)象的dropEffect屬性)為none缕陕,即不允許放下被拖拉的節(jié)點(diǎn),所以如果允許在當(dāng)前節(jié)點(diǎn)drop數(shù)據(jù)疙挺,通常會(huì)使用preventDefault方法扛邑,取消重置拖拉效果為none。
dragleave事件:拖拉離開當(dāng)前節(jié)點(diǎn)范圍時(shí)铐然,在當(dāng)前節(jié)點(diǎn)上觸發(fā)蔬崩,該事件的target屬性是當(dāng)前節(jié)點(diǎn)。在視覺上顯示拖拉離開當(dāng)前節(jié)點(diǎn)锦爵,就在這個(gè)事件的監(jiān)聽函數(shù)中設(shè)置。
drop事件:被拖拉的節(jié)點(diǎn)或選中的文本奥裸,釋放到目標(biāo)節(jié)點(diǎn)時(shí)险掀,在目標(biāo)節(jié)點(diǎn)上觸發(fā)。注意湾宙,如果當(dāng)前節(jié)點(diǎn)不允許drop樟氢,即使在該節(jié)點(diǎn)上方松開鼠標(biāo)鍵冈绊,也不會(huì)觸發(fā)該事件。如果用戶按下Escape鍵埠啃,取消這個(gè)操作死宣,也不會(huì)觸發(fā)該事件。該事件的監(jiān)聽函數(shù)負(fù)責(zé)取出拖拉數(shù)據(jù)碴开,并進(jìn)行相關(guān)處理毅该。
關(guān)于拖拉事件,有以下幾點(diǎn)注意事項(xiàng)潦牛。
拖拉過程只觸發(fā)以上這些拖拉事件眶掌,盡管鼠標(biāo)在移動(dòng),但是鼠標(biāo)事件不會(huì)觸發(fā)巴碗。
將文件從操作系統(tǒng)拖拉進(jìn)瀏覽器朴爬,不會(huì)觸發(fā)dragStart和dragend事件。
dragenter和dragover事件的監(jiān)聽函數(shù)橡淆,用來指定可以放下(drop)拖拉的數(shù)據(jù)召噩。由于網(wǎng)頁(yè)的大部分區(qū)域不適合作為drop的目標(biāo)節(jié)點(diǎn),所以這兩個(gè)事件的默認(rèn)設(shè)置為當(dāng)前節(jié)點(diǎn)不允許drop逸爵。如果想要在目標(biāo)節(jié)點(diǎn)上drop拖拉的數(shù)據(jù)具滴,首先必須阻止這兩個(gè)事件的默認(rèn)行為,或者取消這兩個(gè)事件痊银。
上面代碼中抵蚊,如果不取消拖拉事件或者阻止默認(rèn)行為,就不可能在div節(jié)點(diǎn)上drop被拖拉的節(jié)點(diǎn)溯革。
拖拉事件用一個(gè)DragEvent對(duì)象表示贞绳,該對(duì)象繼承MouseEvent對(duì)象,因此也就繼承了UIEvent和Event對(duì)象致稀。DragEvent對(duì)象只有一個(gè)獨(dú)有的屬性DataTransfer冈闭,其他都是繼承的屬性。DataTransfer屬性用來讀寫拖拉事件中傳輸?shù)臄?shù)據(jù)抖单,詳見下文《DataTransfer對(duì)象》的部分萎攒。
下面的例子展示,如何動(dòng)態(tài)改變被拖動(dòng)節(jié)點(diǎn)的背景色矛绘。
div.addEventListener("dragstart", function(e) {
? this.style.backgroundColor = "red";
}, false);
div.addEventListener("dragend", function(e) {
? this.style.backgroundColor = "green";
}, false);
上面代碼中耍休,div節(jié)點(diǎn)被拖動(dòng)時(shí),背景色會(huì)變?yōu)榧t色货矮,拖動(dòng)結(jié)束羊精,又變回綠色。
下面是一個(gè)例子囚玫,顯示如何實(shí)現(xiàn)將一個(gè)節(jié)點(diǎn)從當(dāng)前父節(jié)點(diǎn)喧锦,拖拉到另一個(gè)父節(jié)點(diǎn)中读规。
// HTML代碼為
//
//? ?
//? ? ? 該節(jié)點(diǎn)可拖拉
//
//
//
//
//
// 被拖拉節(jié)點(diǎn)
var dragged;
document.addEventListener("dragstart", function( event ) {
? // 保存被拖拉節(jié)點(diǎn)
? dragged = event.target;
? // 被拖拉節(jié)點(diǎn)的背景色變透明
? event.target.style.opacity = .5;
}, false);
document.addEventListener("dragend", function( event ) {
? // 被拖拉節(jié)點(diǎn)的背景色恢復(fù)正常
? event.target.style.opacity = "";
}, false);
document.addEventListener("dragover", function( event ) {
? // 防止拖拉效果被重置,允許被拖拉的節(jié)點(diǎn)放入目標(biāo)節(jié)點(diǎn)
? event.preventDefault();
}, false);
document.addEventListener("dragenter", function( event ) {
? // 目標(biāo)節(jié)點(diǎn)的背景色變紫色
? // 由于該事件會(huì)冒泡燃少,所以要過濾節(jié)點(diǎn)
? if ( event.target.className == "dropzone" ) {
? ? event.target.style.background = "purple";
? }
}, false);
document.addEventListener("dragleave", function( event ) {
? // 目標(biāo)節(jié)點(diǎn)的背景色恢復(fù)原樣
? if ( event.target.className == "dropzone" ) {
? ? event.target.style.background = "";
? }
}, false);
document.addEventListener("drop", function( event ) {
? // 防止事件默認(rèn)行為(比如某些Elment節(jié)點(diǎn)上可以打開鏈接)
? event.preventDefault();
? if ( event.target.className == "dropzone" ) {
? ? // 恢復(fù)目標(biāo)節(jié)點(diǎn)背景色
? ? event.target.style.background = "";
? ? // 將被拖拉節(jié)點(diǎn)插入目標(biāo)節(jié)點(diǎn)
? ? dragged.parentNode.removeChild( dragged );
? ? event.target.appendChild( dragged );
? }
}, false);
DataTransfer對(duì)象概述
所有的拖拉事件都有一個(gè)dataTransfer屬性束亏,用來保存需要傳遞的數(shù)據(jù)。這個(gè)屬性的值是一個(gè)DataTransfer對(duì)象阵具。
拖拉的數(shù)據(jù)保存兩方面的數(shù)據(jù):數(shù)據(jù)的種類(又稱格式)和數(shù)據(jù)的值碍遍。數(shù)據(jù)的種類是一個(gè)MIME字符串,比如 text/plain或者image/jpeg怔昨,數(shù)據(jù)的值是一個(gè)字符串雀久。一般來說,如果拖拉一段文本趁舀,則數(shù)據(jù)默認(rèn)就是那段文本赖捌;如果拖拉一個(gè)鏈接,則數(shù)據(jù)默認(rèn)就是鏈接的URL矮烹。
當(dāng)拖拉事件開始的時(shí)候越庇,可以提供數(shù)據(jù)類型和數(shù)據(jù)值;在拖拉過程中奉狈,通過dragenter和dragover事件的監(jiān)聽函數(shù)卤唉,檢查數(shù)據(jù)類型,以確定是否允許放下(drop)被拖拉的對(duì)象仁期。比如桑驱,在只允許放下鏈接的區(qū)域,檢查拖拉的數(shù)據(jù)類型是否為text/uri-list跛蛋。
發(fā)生drop事件時(shí)熬的,監(jiān)聽函數(shù)取出拖拉的數(shù)據(jù),對(duì)其進(jìn)行處理赊级。
DataTransfer對(duì)象的屬性
DataTransfer對(duì)象有以下屬性押框。
(1)dropEffect
dropEffect屬性設(shè)置放下(drop)被拖拉節(jié)點(diǎn)時(shí)的效果,可能的值包括copy(復(fù)制被拖拉的節(jié)點(diǎn))理逊、move(移動(dòng)被拖拉的節(jié)點(diǎn))橡伞、link(創(chuàng)建指向被拖拉的節(jié)點(diǎn)的鏈接)、none(無法放下被拖拉的節(jié)點(diǎn))晋被。設(shè)置除此以外的值兑徘,都是無效的。
target.addEventListener('dragover', function(e) {
? e.preventDefault();
? e.stopPropagation();
? e.dataTransfer.dropEffect = 'copy';
});
dropEffect屬性一般在dragenter和dragover事件的監(jiān)聽函數(shù)中設(shè)置羡洛,對(duì)于dragstart挂脑、drag、dragleave這三個(gè)事件,該屬性不起作用最域。進(jìn)入目標(biāo)節(jié)點(diǎn)后,拖拉行為會(huì)初始化成用戶設(shè)定的效果锈麸,用戶可以通過按下Shift鍵和Control鍵镀脂,改變初始設(shè)置,在copy忘伞、move薄翅、link三種效果中切換。
鼠標(biāo)箭頭會(huì)根據(jù)dropEffect屬性改變形狀氓奈,提示目前正處于哪一種效果翘魄。這意味著,通過鼠標(biāo)就能判斷是否可以在當(dāng)前節(jié)點(diǎn)drop被拖拉的節(jié)點(diǎn)舀奶。
(2)effectAllowed
effectAllowed屬性設(shè)置本次拖拉中允許的效果暑竟,可能的值包括copy(復(fù)制被拖拉的節(jié)點(diǎn))、move(移動(dòng)被拖拉的節(jié)點(diǎn))育勺、link(創(chuàng)建指向被拖拉節(jié)點(diǎn)的鏈接)但荤、copyLink(允許copy或link)、copyMove(允許copy或move)涧至、linkMove(允許link或move)腹躁、all(允許所有效果)、none(無法放下被拖拉的節(jié)點(diǎn))南蓬、uninitialized(默認(rèn)值纺非,等同于all)。如果某種效果是不允許的赘方,用戶就無法在目標(biāo)節(jié)點(diǎn)中達(dá)成這種效果烧颖。
dragstart事件的監(jiān)聽函數(shù),可以設(shè)置被拖拉節(jié)點(diǎn)允許的效果蒜焊;dragenter和dragover事件的監(jiān)聽函數(shù)倒信,可以設(shè)置目標(biāo)節(jié)點(diǎn)允許的效果。
event.dataTransfer.effectAllowed = "copy";
dropEffect屬性和effectAllowed屬性泳梆,往往配合使用鳖悠。
event.dataTransfer.effectAllowed = "copyMove";
event.dataTransfer.dropEffect = "copy";
上面代碼中,copy是指定的效果优妙,但是可以通過Shift或Ctrl鍵(根據(jù)平臺(tái)而定)乘综,將效果切換成move。
只要dropEffect屬性和effectAllowed屬性之中套硼,有一個(gè)為none卡辰,就無法在目標(biāo)節(jié)點(diǎn)上完成drop操作。
(3)files
files屬性是一個(gè)FileList對(duì)象,包含一組本地文件九妈,可以用來在拖拉操作中傳送反砌。如果本次拖拉不涉及文件,則屬性為空的FileList對(duì)象萌朱。
下面就是一個(gè)接收拖拉文件的例子宴树。
// HTML代碼為
//
//? 文件拖拉到這里
//
var div = document.getElementById('output');
div.addEventListener("dragenter", function( event ) {
? div.textContent = '';
? event.stopPropagation();
? event.preventDefault();
}, false);
div.addEventListener("dragover", function( event ) {
? event.stopPropagation();
? event.preventDefault();
}, false);
div.addEventListener("drop", function( event ) {
? event.stopPropagation();
? event.preventDefault();
? var files = event.dataTransfer.files;
? for (var i = 0; i < files.length; i++) {
? ? div.textContent += files[i].name + ' ' + files[i].size + '字節(jié)\n';
? }
}, false);
上面代碼中,通過files屬性讀取拖拉文件的信息晶疼。如果想要讀取文件內(nèi)容酒贬,就要使用FileReader對(duì)象。
div.addEventListener('drop', function(e) {
? e.preventDefault();
? e.stopPropagation();
? var fileList = e.dataTransfer.files;
? if (fileList.length > 0) {
? ? var file = fileList[0];
? ? var reader = new FileReader();
? ? reader.onloadend = function(e) {
? ? ? if (e.target.readyState == FileReader.DONE) {
? ? ? ? var content = reader.result;
? ? ? ? contentDiv.innerHTML = "File: " + file.name + "\n\n" + content;
? ? ? }
? ? }
? ? reader.readAsBinaryString(file);
? }
});
(4)types
types屬性是一個(gè)數(shù)組翠霍,保存每一次拖拉的數(shù)據(jù)格式锭吨,比如拖拉文件,則格式信息就為File寒匙。
下面是一個(gè)例子零如,通過檢查dataTransfer屬性的類型,決定是否允許在當(dāng)前節(jié)點(diǎn)執(zhí)行drop操作锄弱。
function contains(list, value){
? for( var i = 0; i < list.length; ++i ){
? ? if(list[i] === value) return true;
? }
? return false;
}
function doDragOver(event){
? var isLink = contains( event.dataTransfer.types, "text/uri-list");
? if (isLink) event.preventDefault();
}
上面代碼中埠况,只有當(dāng)被拖拉的節(jié)點(diǎn)是一個(gè)鏈接時(shí),才允許在當(dāng)前節(jié)點(diǎn)放下棵癣。
DataTransfer對(duì)象的方法
DataTransfer對(duì)象有以下方法辕翰。
(1)setData()
setData方法用來設(shè)置事件所帶有的指定類型的數(shù)據(jù)。它接受兩個(gè)參數(shù)狈谊,第一個(gè)是數(shù)據(jù)類型喜命,第二個(gè)是具體數(shù)據(jù)。如果指定的類型在現(xiàn)有數(shù)據(jù)中不存在河劝,則該類型將寫入types屬性壁榕;如果已經(jīng)存在,在該類型的現(xiàn)有數(shù)據(jù)將被替換赎瞎。
event.dataTransfer.setData("text/plain", "Text to drag");
上面代碼為事件加入純文本格式的數(shù)據(jù)牌里。
如果拖拉文本框或者拖拉選中的文本,會(huì)默認(rèn)將文本數(shù)據(jù)添加到dataTransfer屬性务甥,不用手動(dòng)指定牡辽。
event.dataTransfer.setData('text/plain', 'bbb')">
aaa
上面代碼中,拖拉數(shù)據(jù)實(shí)際上是bbb敞临,而不是aaa态辛。
下面是添加其他類型的數(shù)據(jù)。由于text/plain是最普遍支持的格式挺尿,為了保證兼容性奏黑,建議最后總是將數(shù)據(jù)保存一份純文本的格式炊邦。
var dt = event.dataTransfer;
// 添加鏈接
dt.setData("text/uri-list", "http://www.example.com");
dt.setData("text/plain", "http://www.example.com");
// 添加HTML代碼
dt.setData("text/html", "Hello there, stranger");
dt.setData("text/plain", "Hello there, stranger");
// 添加圖像的URL
dt.setData("text/uri-list", imageurl);
dt.setData("text/plain", imageurl);
可以一次提供多種格式的數(shù)據(jù)。
var dt = event.dataTransfer;
dt.setData("application/x-bookmark", bookmarkString);
dt.setData("text/uri-list", "http://www.example.com");
dt.setData("text/plain", "http://www.example.com");
上面代碼中熟史,通過在同一個(gè)事件上面馁害,存放三種類型的數(shù)據(jù),使得拖拉事件可以在不同的對(duì)象上面蹂匹,drop不同的值蜗细。注意,第一種格式是一個(gè)自定義格式怒详,瀏覽器默認(rèn)無法讀取,這意味著踪区,只有某個(gè)部署了特定代碼的節(jié)點(diǎn)昆烁,才可能drop(讀取到)這個(gè)數(shù)據(jù)。
(2)getData()
getData方法接受一個(gè)字符串(表示數(shù)據(jù)類型)作為參數(shù)缎岗,返回事件所帶的指定類型的數(shù)據(jù)(通常是用setData方法添加的數(shù)據(jù))静尼。如果指定類型的數(shù)據(jù)不存在,則返回空字符串传泊。通常只有drop事件觸發(fā)后鼠渺,才能取出數(shù)據(jù)。如果取出另一個(gè)域名存放的數(shù)據(jù)眷细,將會(huì)報(bào)錯(cuò)拦盹。
下面是一個(gè)drop事件的監(jiān)聽函數(shù),用來取出指定類型的數(shù)據(jù)溪椎。
function onDrop(event){
? var data = event.dataTransfer.getData("text/plain");
? event.target.textContent = data;
? event.preventDefault();
}
上面代碼取出拖拉事件的文本數(shù)據(jù)普舆,將其替換成當(dāng)前節(jié)點(diǎn)的文本內(nèi)容。注意校读,這時(shí)還必須取消瀏覽器的默認(rèn)行為沼侣,因?yàn)榧偃缬脩敉侠氖且粋€(gè)鏈接,瀏覽器默認(rèn)會(huì)在當(dāng)前窗口打開這個(gè)鏈接歉秫。
getData方法返回的是一個(gè)字符串蛾洛,如果其中包含多項(xiàng)數(shù)據(jù),就必須手動(dòng)解析雁芙。
function doDrop(event){
? var lines = event.dataTransfer.getData("text/uri-list").split("\n");
? for (let line of lines) {
? ? let link = document.createElement("a");
? ? link.href = line;
? ? link.textContent = line;
? ? event.target.appendChild(link);
? }
? event.preventDefault();
}
上面代碼中轧膘,getData方法返回的是一組鏈接,就必須自行解析兔甘。
類型值指定為URL扶供,可以取出第一個(gè)有效鏈接。
var link = event.dataTransfer.getData("URL");
下面是一次性取出多種類型的數(shù)據(jù)裂明。
function doDrop(event){
? var types = event.dataTransfer.types;
? var supportedTypes = ["text/uri-list", "text/plain"];
? types = supportedTypes.filter(function (value) types.includes(value));
? if (types.length)
? ? var data = event.dataTransfer.getData(types[0]);
? event.preventDefault();
}
(3)clearData()
clearData方法接受一個(gè)字符串(表示數(shù)據(jù)類型)作為參數(shù)椿浓,刪除事件所帶的指定類型的數(shù)據(jù)太援。如果沒有指定類型,則刪除所有數(shù)據(jù)扳碍。如果指定類型不存在提岔,則原數(shù)據(jù)不受影響。
event.dataTransfer.clearData("text/uri-list");
上面代碼清除事件所帶的URL數(shù)據(jù)笋敞。
(4)setDragImage()
拖動(dòng)過程中(dragstart事件觸發(fā)后)碱蒙,瀏覽器會(huì)顯示一張圖片跟隨鼠標(biāo)一起移動(dòng),表示被拖動(dòng)的節(jié)點(diǎn)夯巷。這張圖片是自動(dòng)創(chuàng)造的赛惩,通常顯示為被拖動(dòng)節(jié)點(diǎn)的外觀,不需要自己動(dòng)手設(shè)置趁餐。setDragImage方法可以用來自定義這張圖片喷兼,它接受三個(gè)參數(shù),第一個(gè)是img圖片元素或者canvas元素后雷,如果省略或?yàn)閚ull則使用被拖動(dòng)的節(jié)點(diǎn)的外觀季惯,第二個(gè)和第三個(gè)參數(shù)為鼠標(biāo)相對(duì)于該圖片左上角的橫坐標(biāo)和右坐標(biāo)。
下面是一個(gè)例子臀突。
// HTML代碼為
//
drag me
//
var div = document.getElementById("drag-with-image");
div.addEventListener("dragstart", function(e) {
? var img = document.createElement("img");
? img.src = "http://path/to/img";
? e.dataTransfer.setDragImage(img, 0, 0);
}, false);
謝謝關(guān)注勉抓!