原聲拖拽

一、介紹

mousedown mouseup mousemove

dragstart dragend drag (在拖動元素上執行) 首先要在此元素上設置draggable="true" 為可拖

dragenter dragleave? dragover (在目標元素上執行)

drop 放下 (在目標元素上執行)

dropzone表示可以放到哪個區域

test



上面三個都有類似之處

transfer 轉移

二、代碼

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腳本:

// 拖動元素

var eleBox = document.getElementById('box');

eleBox.addEventListener('dragstart', function(e) {

? ? e.dataTransfer.setData('ele', e.target.id);? // 獲取拖動元素的id

});

eleBox.addEventListener('drag', function(e) {

? ? console.log('持續拖動!');

});

eleBox.addEventListener('dragend', function(e) {

? ? console.log('結束拖動!');

});

// 目錄元素

var eleBoxWrap = document.getElementById('box-wrap');

eleBoxWrap.addEventListener('dragenter', function(e) {

? ? console.log('拖動元素已進入此容器!');

});

eleBoxWrap.addEventListener('dragover', function(e) {

? ? console.log('拖動元素懸浮在此容器上!');

? ? e.preventDefault();

});

eleBoxWrap.addEventListener('dragleave', function(e) {

? ? console.log('拖動元素已離開此容器!');

});

eleBoxWrap.addEventListener('drop', function(e) {

? ? e.preventDefault();

? ? var data = e.dataTransfer.getData('ele');? // 獲取拖動元素的id

? ? e.target.appendChild(document.getElementById(data));

? ? console.log('放下')

});

詳細介紹(源自http://javascript.ruanyifeng.com/dom/event.html)

當Element節點或選中的文本被拖拉時,就會持續觸發拖拉事件,包括以下一些事件。

drag事件:拖拉過程中,在被拖拉的節點上持續觸發。

dragstart事件:拖拉開始時在被拖拉的節點上觸發,該事件的target屬性是被拖拉的節點。通常應該在這個事件的監聽函數中,指定拖拉的數據。

dragend事件:拖拉結束時(釋放鼠標鍵或按下escape鍵)在被拖拉的節點上觸發,該事件的target屬性是被拖拉的節點。它與dragStart事件,在同一個節點上觸發。不管拖拉是否跨窗口,或者中途被取消,dragend事件總是會觸發的。

dragenter事件:拖拉進入當前節點時,在當前節點上觸發,該事件的target屬性是當前節點。通常應該在這個事件的監聽函數中,指定是否允許在當前節點放下(drop)拖拉的數據。如果當前節點沒有該事件的監聽函數,或者監聽函數不執行任何操作,就意味著不允許在當前節點放下數據。在視覺上顯示拖拉進入當前節點,也是在這個事件的監聽函數中設置。

dragover事件:拖拉到當前節點上方時,在當前節點上持續觸發,該事件的target屬性是當前節點。該事件與dragenter事件基本類似,默認會重置當前的拖拉事件的效果(DataTransfer對象的dropEffect屬性)為none,即不允許放下被拖拉的節點,所以如果允許在當前節點drop數據,通常會使用preventDefault方法,取消重置拖拉效果為none。

dragleave事件:拖拉離開當前節點范圍時,在當前節點上觸發,該事件的target屬性是當前節點。在視覺上顯示拖拉離開當前節點,就在這個事件的監聽函數中設置。

drop事件:被拖拉的節點或選中的文本,釋放到目標節點時,在目標節點上觸發。注意,如果當前節點不允許drop,即使在該節點上方松開鼠標鍵,也不會觸發該事件。如果用戶按下Escape鍵,取消這個操作,也不會觸發該事件。該事件的監聽函數負責取出拖拉數據,并進行相關處理。

關于拖拉事件,有以下幾點注意事項。

拖拉過程只觸發以上這些拖拉事件,盡管鼠標在移動,但是鼠標事件不會觸發。

將文件從操作系統拖拉進瀏覽器,不會觸發dragStart和dragend事件。

dragenter和dragover事件的監聽函數,用來指定可以放下(drop)拖拉的數據。由于網頁的大部分區域不適合作為drop的目標節點,所以這兩個事件的默認設置為當前節點不允許drop。如果想要在目標節點上drop拖拉的數據,首先必須阻止這兩個事件的默認行為,或者取消這兩個事件。



上面代碼中,如果不取消拖拉事件或者阻止默認行為,就不可能在div節點上drop被拖拉的節點。

拖拉事件用一個DragEvent對象表示,該對象繼承MouseEvent對象,因此也就繼承了UIEvent和Event對象。DragEvent對象只有一個獨有的屬性DataTransfer,其他都是繼承的屬性。DataTransfer屬性用來讀寫拖拉事件中傳輸的數據,詳見下文《DataTransfer對象》的部分。

下面的例子展示,如何動態改變被拖動節點的背景色。

div.addEventListener("dragstart", function(e) {

? this.style.backgroundColor = "red";

}, false);

div.addEventListener("dragend", function(e) {

? this.style.backgroundColor = "green";

}, false);

上面代碼中,div節點被拖動時,背景色會變為紅色,拖動結束,又變回綠色。

下面是一個例子,顯示如何實現將一個節點從當前父節點,拖拉到另一個父節點中。

// HTML代碼為

//


//? ?


//? ? ? 該節點可拖拉

//

//

//


//


//



// 被拖拉節點

var dragged;

document.addEventListener("dragstart", function( event ) {

? // 保存被拖拉節點

? dragged = event.target;

? // 被拖拉節點的背景色變透明

? event.target.style.opacity = .5;

}, false);

document.addEventListener("dragend", function( event ) {

? // 被拖拉節點的背景色恢復正常

? event.target.style.opacity = "";

}, false);

document.addEventListener("dragover", function( event ) {

? // 防止拖拉效果被重置,允許被拖拉的節點放入目標節點

? event.preventDefault();

}, false);

document.addEventListener("dragenter", function( event ) {

? // 目標節點的背景色變紫色

? // 由于該事件會冒泡,所以要過濾節點

? if ( event.target.className == "dropzone" ) {

? ? event.target.style.background = "purple";

? }

}, false);

document.addEventListener("dragleave", function( event ) {

? // 目標節點的背景色恢復原樣

? if ( event.target.className == "dropzone" ) {

? ? event.target.style.background = "";

? }

}, false);

document.addEventListener("drop", function( event ) {

? // 防止事件默認行為(比如某些Elment節點上可以打開鏈接)

? event.preventDefault();

? if ( event.target.className == "dropzone" ) {

? ? // 恢復目標節點背景色

? ? event.target.style.background = "";

? ? // 將被拖拉節點插入目標節點

? ? dragged.parentNode.removeChild( dragged );

? ? event.target.appendChild( dragged );

? }

}, false);

DataTransfer對象概述

所有的拖拉事件都有一個dataTransfer屬性,用來保存需要傳遞的數據。這個屬性的值是一個DataTransfer對象。

拖拉的數據保存兩方面的數據:數據的種類(又稱格式)和數據的值。數據的種類是一個MIME字符串,比如 text/plain或者image/jpeg,數據的值是一個字符串。一般來說,如果拖拉一段文本,則數據默認就是那段文本;如果拖拉一個鏈接,則數據默認就是鏈接的URL。

當拖拉事件開始的時候,可以提供數據類型和數據值;在拖拉過程中,通過dragenter和dragover事件的監聽函數,檢查數據類型,以確定是否允許放下(drop)被拖拉的對象。比如,在只允許放下鏈接的區域,檢查拖拉的數據類型是否為text/uri-list。

發生drop事件時,監聽函數取出拖拉的數據,對其進行處理。

DataTransfer對象的屬性

DataTransfer對象有以下屬性。

(1)dropEffect

dropEffect屬性設置放下(drop)被拖拉節點時的效果,可能的值包括copy(復制被拖拉的節點)、move(移動被拖拉的節點)、link(創建指向被拖拉的節點的鏈接)、none(無法放下被拖拉的節點)。設置除此以外的值,都是無效的。

target.addEventListener('dragover', function(e) {

? e.preventDefault();

? e.stopPropagation();

? e.dataTransfer.dropEffect = 'copy';

});

dropEffect屬性一般在dragenter和dragover事件的監聽函數中設置,對于dragstart、drag、dragleave這三個事件,該屬性不起作用。進入目標節點后,拖拉行為會初始化成用戶設定的效果,用戶可以通過按下Shift鍵和Control鍵,改變初始設置,在copy、move、link三種效果中切換。

鼠標箭頭會根據dropEffect屬性改變形狀,提示目前正處于哪一種效果。這意味著,通過鼠標就能判斷是否可以在當前節點drop被拖拉的節點。

(2)effectAllowed

effectAllowed屬性設置本次拖拉中允許的效果,可能的值包括copy(復制被拖拉的節點)、move(移動被拖拉的節點)、link(創建指向被拖拉節點的鏈接)、copyLink(允許copy或link)、copyMove(允許copy或move)、linkMove(允許link或move)、all(允許所有效果)、none(無法放下被拖拉的節點)、uninitialized(默認值,等同于all)。如果某種效果是不允許的,用戶就無法在目標節點中達成這種效果。

dragstart事件的監聽函數,可以設置被拖拉節點允許的效果;dragenter和dragover事件的監聽函數,可以設置目標節點允許的效果。

event.dataTransfer.effectAllowed = "copy";

dropEffect屬性和effectAllowed屬性,往往配合使用。

event.dataTransfer.effectAllowed = "copyMove";

event.dataTransfer.dropEffect = "copy";

上面代碼中,copy是指定的效果,但是可以通過Shift或Ctrl鍵(根據平臺而定),將效果切換成move。

只要dropEffect屬性和effectAllowed屬性之中,有一個為none,就無法在目標節點上完成drop操作。

(3)files

files屬性是一個FileList對象,包含一組本地文件,可以用來在拖拉操作中傳送。如果本次拖拉不涉及文件,則屬性為空的FileList對象。

下面就是一個接收拖拉文件的例子。

// 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 + '字節\n';

? }

}, false);

上面代碼中,通過files屬性讀取拖拉文件的信息。如果想要讀取文件內容,就要使用FileReader對象。

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屬性是一個數組,保存每一次拖拉的數據格式,比如拖拉文件,則格式信息就為File。

下面是一個例子,通過檢查dataTransfer屬性的類型,決定是否允許在當前節點執行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();

}

上面代碼中,只有當被拖拉的節點是一個鏈接時,才允許在當前節點放下。

DataTransfer對象的方法

DataTransfer對象有以下方法。

(1)setData()

setData方法用來設置事件所帶有的指定類型的數據。它接受兩個參數,第一個是數據類型,第二個是具體數據。如果指定的類型在現有數據中不存在,則該類型將寫入types屬性;如果已經存在,在該類型的現有數據將被替換。

event.dataTransfer.setData("text/plain", "Text to drag");

上面代碼為事件加入純文本格式的數據。

如果拖拉文本框或者拖拉選中的文本,會默認將文本數據添加到dataTransfer屬性,不用手動指定。


event.dataTransfer.setData('text/plain', 'bbb')">

aaa


上面代碼中,拖拉數據實際上是bbb,而不是aaa。

下面是添加其他類型的數據。由于text/plain是最普遍支持的格式,為了保證兼容性,建議最后總是將數據保存一份純文本的格式。

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);

可以一次提供多種格式的數據。

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");

上面代碼中,通過在同一個事件上面,存放三種類型的數據,使得拖拉事件可以在不同的對象上面,drop不同的值。注意,第一種格式是一個自定義格式,瀏覽器默認無法讀取,這意味著,只有某個部署了特定代碼的節點,才可能drop(讀取到)這個數據。

(2)getData()

getData方法接受一個字符串(表示數據類型)作為參數,返回事件所帶的指定類型的數據(通常是用setData方法添加的數據)。如果指定類型的數據不存在,則返回空字符串。通常只有drop事件觸發后,才能取出數據。如果取出另一個域名存放的數據,將會報錯。

下面是一個drop事件的監聽函數,用來取出指定類型的數據。

function onDrop(event){

? var data = event.dataTransfer.getData("text/plain");

? event.target.textContent = data;

? event.preventDefault();

}

上面代碼取出拖拉事件的文本數據,將其替換成當前節點的文本內容。注意,這時還必須取消瀏覽器的默認行為,因為假如用戶拖拉的是一個鏈接,瀏覽器默認會在當前窗口打開這個鏈接。

getData方法返回的是一個字符串,如果其中包含多項數據,就必須手動解析。

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,可以取出第一個有效鏈接。

var link = event.dataTransfer.getData("URL");

下面是一次性取出多種類型的數據。

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方法接受一個字符串(表示數據類型)作為參數,刪除事件所帶的指定類型的數據。如果沒有指定類型,則刪除所有數據。如果指定類型不存在,則原數據不受影響。

event.dataTransfer.clearData("text/uri-list");

上面代碼清除事件所帶的URL數據。

(4)setDragImage()

拖動過程中(dragstart事件觸發后),瀏覽器會顯示一張圖片跟隨鼠標一起移動,表示被拖動的節點。這張圖片是自動創造的,通常顯示為被拖動節點的外觀,不需要自己動手設置。setDragImage方法可以用來自定義這張圖片,它接受三個參數,第一個是img圖片元素或者canvas元素,如果省略或為null則使用被拖動的節點的外觀,第二個和第三個參數為鼠標相對于該圖片左上角的橫坐標和右坐標。

下面是一個例子。

// 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);

謝謝關注!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容