HTML5 Drag and Drop API

這篇文章說明拖曳功能的基本概念。

建立可拖曳的內容

在大部分瀏覽器中,文字選取、圖片和連結皆預設可以拖曳。 舉例來說,如果您將連結拖曳到網頁上,就會看到一個 你可以將標題和網址放在網址列或桌面上 捷徑或點選連結。如要將其他類型的內容設為可拖曳, 都必須使用 HTML5 拖曳 API

如要將物件設為可拖曳,請在該元素上設定 draggable=true。大約在 任何項目都可以支援拖曳功能,包括圖片、檔案、連結、檔案或任何 標記。

以下範例建立介面來重新排列 設計成品資料欄的基本標記如下所示,如下所示: 分別將 draggable 屬性設為 true

<div class="container">
  <div draggable="true" class="box">A</div>
  <div draggable="true" class="box">B</div>
  <div draggable="true" class="box">C</div>
</div>

以下是容器和方塊元素的 CSS。唯一與該 CSS 供應商 拖曳功能是cursor: move 資源。程式碼的其餘部分可控制容器的版面配置和樣式 和 Box 元素

.container {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
}

.box {
  border: 3px solid #666;
  background-color: #ddd;
  border-radius: .5em;
  padding: 10px;
  cursor: move;
}

您可以拖曳項目,但不會執行其他動作。新增 您必須採用 JavaScript API

監聽拖曳事件

如要監控拖曳程序,您可以監聽下列任一事件:

如要處理拖曳流程,您需要某種來源元素 (拖曳 資料酬載 (拖曳的項目) 和目標 ( 防止掉落)。來源元素幾乎可以是任何類型的元素。 「目標」是可接受使用者資料的放置區域或一組捨棄區域 試圖丟棄。並非所有元素都能做為目標目標。舉例來說,您無法指定 可以是圖片

開始及結束拖曳序列

為內容定義 draggable="true" 屬性後, dragstart 事件處理常式,啟動各欄的拖曳順序。

這段程式碼會在使用者開始拖曳資料欄時,將資料欄的透明度設為 40%。 然後在拖曳事件結束時將間隔回到 100%

function handleDragStart(e) {
  this.style.opacity = '0.4';
}

function handleDragEnd(e) {
  this.style.opacity = '1';
}

let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
  item.addEventListener('dragstart', handleDragStart);
  item.addEventListener('dragend', handleDragEnd);
});

您可以前往下方的 Glitch 示範頁面查看結果。拖曳項目和其項目 不透明度變更。來源元素具有 dragstart 事件,因此設定 this.style.opacity 設為 40% 時,能為使用者提供該元素具有的視覺回饋 正在移動目前選取的項目放置項目時,來源元素 就會傳回 100% 的不透明度。

新增其他視覺提示

如要協助使用者瞭解如何與您的介面互動,請使用 dragenterdragoverdragleave 事件處理常式。在這個範例中, 除了可以拖曳之外,放置目標也是放置目標。協助使用者: 只要將拖曳項目移到某個位置上, 。舉例來說,在 CSS 中,您可以建立 over 類別 放置目標的元素:

.box.over {
  border: 3px dotted #666;
}

接著,請在 JavaScript 中設定事件處理常式,並在發生以下情況時新增 over 類別: 拖曳該欄,並在拖曳的元素離開時移除。於 我們也會確實移除 dragend 處理常式的結尾類別 拖曳

document.addEventListener('DOMContentLoaded', (event) => {

  function handleDragStart(e) {
    this.style.opacity = '0.4';
  }

  function handleDragEnd(e) {
    this.style.opacity = '1';

    items.forEach(function (item) {
      item.classList.remove('over');
    });
  }

  function handleDragOver(e) {
    e.preventDefault();
    return false;
  }

  function handleDragEnter(e) {
    this.classList.add('over');
  }

  function handleDragLeave(e) {
    this.classList.remove('over');
  }

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });
});

在這個程式碼中,有幾個要點可以說明:

  • 預設動作 dragover 事件的 dataTransfer.dropEffect 屬性是設為 "none"。本頁稍後會介紹 dropEffect 屬性。目前, 只需要知道這會導致無法觸發 drop 事件。如要覆寫這項設定 行為並呼叫 e.preventDefault()。另一個建議做法 false

  • dragenter 事件處理常式的用途是切換 over 類別,而非 dragover。如果使用 dragover,事件會在使用者同時重複觸發 將拖曳的項目保留在資料欄中,讓 CSS 類別切換 重複執行。這樣瀏覽器就會執行許多不必要的轉譯工作 而會影響使用者體驗強烈建議您盡可能減少 重新繪製。如需使用 dragover,請考慮 節流或取消彈跳事件監聽器

開賽

如要處理放置作業,請為 drop 事件新增事件監聽器。在「drop」中 處理常式,您需要防止瀏覽器預設的 ANR 行為; 通常是惱人的重新導向方法是呼叫 e.stopPropagation()

function handleDrop(e) {
  e.stopPropagation(); // stops the browser from redirecting.
  return false;
}

請務必註冊新的處理常式與其他處理常式:

  let items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
    item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('dragenter', handleDragEnter);
    item.addEventListener('dragleave', handleDragLeave);
    item.addEventListener('dragend', handleDragEnd);
    item.addEventListener('drop', handleDrop);
  });

如果您現在執行程式碼,項目不會拖放至新的位置。目的地: 要啟動函式,請使用 DataTransfer 物件。

dataTransfer 屬性會保留透過拖曳動作傳送的資料。dataTransfer 是在 dragstart 事件中設定,並在放置事件中讀取或處理。撥號中 e.dataTransfer.setData(mimeType, dataPayload) 可讓您設定物件的 MIME 類型和資料酬載

在本例中,我們將讓使用者重新排列資料欄的順序。 要這麼做,您必須先在拖曳時儲存來源元素的 HTML 開始:

function handleDragStart(e) {
  this.style.opacity = '0.4';

  dragSrcEl = this;

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/html', this.innerHTML);
}

drop 事件中,您會設定來源資料欄的 將 HTML 改成您已放置資料的目標資料欄的 HTML。這個 包含確認使用者不會回訪同一欄

function handleDrop(e) {
  e.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = e.dataTransfer.getData('text/html');
  }

  return false;
}

您可以在下方示範中查看結果。如要這麼做,你必須 電腦版瀏覽器。行動裝置不支援 Drag and Drop API。拖曳 變更 B 欄上方的 A 欄,您會發現它們的變化位置:

更多拖曳屬性

dataTransfer 物件會公開屬性,為 會在拖曳過程中控制每個放置目標回應方式 特定資料類型

  • dataTransfer.effectAllowed敬上 限制拖曳方式使用者可對元素執行哪些操作用途 並在拖曳處理模型中初始化 dropEffect dragenterdragover 事件。屬性中可有 下列值:nonecopycopyLinkcopyMovelinklinkMovemovealluninitialized
  • dataTransfer.dropEffect敬上 控制使用者在 dragenterdragover 期間收到的意見回饋 事件。當使用者將遊標懸停在目標元素上時,瀏覽器的 遊標指出即將進行的作業類型,例如副本 或是進行特定動作效果可能為下列其中一個值:nonecopylinkmove
  • e.dataTransfer.setDragImage(imgElement, x, y)敬上 意味著不用使用瀏覽器的預設「ghost 圖片」提供意見,您 可以設定拖曳圖示

上傳檔案

這個簡易範例使用資料欄做為拖曳來源和拖曳目標。這個 會發生於 UI 要求使用者重新排列項目在某些情況下 拖曳目標和來源可能會有不同的元素類型,就像在介面中一樣 使用者必須選取一張圖片做為產品的主要圖片, 將所選圖片拖曳到目標上

使用者經常以拖曳方式將桌面項目拖曳到 負載平衡器的工作就是將使用者流量 平均分配給應用程式的多個執行個體主要差異在於 drop 處理常式。不使用 「dataTransfer.getData()」用於存取檔案,其資料包含在以下位置: dataTransfer.files 屬性:

function handleDrop(e) {
  e.stopPropagation(); // Stops some browsers from redirecting.
  e.preventDefault();

  var files = e.dataTransfer.files;
  for (var i = 0, f; (f = files[i]); i++) {
    // Read the File objects in this FileList.
  }
}

想進一步瞭解這項功能,請前往 自訂拖曳

其他資源