HTML5 drag和drop的親手實踐

起因

最近在公司打雜的時候,突然分到了一個鍋,就是要支持一個新的功能:用戶可以通過拖曳組件來改變組件的順序。因此,這陣子就看了一下網上的一些drag和drog的文章以及W3C的介紹,然后自己親手實踐了一下,畢竟打碼,才能變得更強。
首先,先放一個我的demo,大家可以去那里隨便拖動一下玩一玩:
這是一個樣例

知識儲備

與drag和drog有關的屬性和事件

  • draggable屬性: 如果你想讓一個元素變得可以拖曳的話,那么你就必須設置它的draggable=true,如下
<div class='target' draggable="true"></div>

這樣,該元素就可以拖動了

跟拖拽有關的事件如下:

  • ondragstart: 當元素開始被拖動時,觸發該事件,目標對象是被拖動的元素
  • ondragover: 當被拖動元素在懸掛元素上移動的時候,該事件觸發。目標對象是被拖動元素懸掛的那個元素。
  • ondragleave: 當被拖動元素離開懸掛元素時,觸發該事件。目標對象是被拖動元素懸掛的那個元素。
  • ondrop: 當鼠標松開被拖動元素的時候,觸發該事件。目標對象是被拖動元素懸掛的那個元素。
  • ondragend: 當鼠標松開被拖動元素的時候,觸發該事件。目標對象是被拖動的元素。其中,ondrop事件會先于ondragend事件觸發。
  • event.preventDefault: 當觸發ondragover事件的時候,必須使用event.preventDefault(),否則的話,ondrop事件就不會觸發
  • event.dataTransfer.effectAllowed:設置或返回被拖動元素允許發生的拖動行為。可設置的屬性很多,這里我們就不細說,感興趣的可以去查下,一般來說,我們都設置為"move".

插入節點的方法

  • 將節點插入到另一個節點前面,代碼如下
 function insertBefore(insertNode, node) {
       node.parentNode.insertBefore(insertNode, node)
 }

這個其實比較簡單,就是找到節點的父親,然后將要插入的節點放到節點的前面。

  • 將節點插入到另一個節點后面,代碼如下圖
 function insertAfter(insertNode, node) {
       if (node.nextElementSibling) {
         insertBefore(insertNode, node.nextElementSibling)
       } else {
         node.parentNode.appendChild(insertNode)
       }
 }

這個其實也挺簡單的,就是如果該節點有兄弟節點的話,那么就將插入節點放到它兄弟節點的前面,否則,則說明該節點是父節點的最后一個節點,因此直接將插入節點放到父節點的末尾。

實踐

在這里,我們要做的就是一個支持各個圖片拖曳來交換位置的玩意,不過,當圖片交換位置的時候,不單單是圖片交換位置,而是包含圖片的容器交換位置。

1.我們先放置幾張圖片,并且將它們的dragable設置為true,這樣它們就可以拖動了。代碼如下:

<body>
    <div class='target' draggable="true">
      <img src="./imgs/1.jpeg" alt="1">
    </div>
    <div class='target' draggable="true">
      <img src='./imgs/2.jpg' />
    </div>
    <div class='target' draggable="true">
      <img src="./imgs/3.jpg" alt="ss">
    </div>    
    <div class='target' draggable="true">
      <img src="./imgs/4.jpg" alt="ss">
    </div>   
  </body>

效果:


效果

2.為每個div都設置一個ondragstart函數,當該函數觸發的時候,進行初始化操作,比如記錄當前的目標對象,拖動目標的y值,以及設置拖動的效果。

// 拖動的目標對象
let target = ''
// 拖動的目標對象的y值
let targetOffsetTop = 0
// 當元素開始被拖動時,觸發該事件,目標對象是被拖動的元素
function handleDragStart(ev) {
  target = findTarget(ev.target)
  targetOffsetTop = ev.target.offsetTop
  ev.dataTransfer.effectAllowed = 'move'
}
// 找到類名為target的目標對象
function findTarget(node) {
  if (!node || node == document) {
    return null
  }
  if (node.classList.contains('target')) {
    return node;
  }
  return findTarget(node.parentNode)
}

3.為每個div注冊一個ondragover事件和ondragleave事件,在ondragover事件里,主要是調用event.preventDefault來防止ondrog不會被觸發,并且為了看起來更明顯,當ondragover事件觸發的時候,為目標對象增加一個dotted類。當ondragleave事件觸發的時候,則把dotted類從目標對象移除。

// 當被拖動元素在懸掛元素上移動的時候,該事件觸發。目標對象是被拖動元素懸掛的那個元素。
// 必須執行event.preventDefault(),不然的話ondrop不會觸發
function handleDragOver(ev) {
  ev.preventDefault();
  ev.target.classList.add('dotted')
}
// 當被拖動元素離開懸掛元素時,觸發該事件。目標對象是被拖動元素懸掛的那個元素。
function handleDragLeave(ev) {
  ev.target.classList.remove('dotted')
}

4.為每個div注冊ondrog事件和ondragend事件,ondrog事件是重點,它主要是根據被拖動元素和被拖動元素懸掛的那個元素的坐標,來決定是要將被拖動元素插入到懸掛元素的前面還是后面。而ondragend主要是用于將target設置為null,代碼如下:

// 當鼠標松開被拖動元素的時候,觸發該事件。目標對象是被拖動元素懸掛的那個元素。
function handleDrog(ev) {
  let resultOffsetTop = ev.target.offsetTop
  if (targetOffsetTop < resultOffsetTop) {
    insertAfter(target, findTarget(ev.target))
  }
  else {
    insertBefore(target, findTarget(ev.target))
  }
  ev.target.classList.remove('dotted')
}
// 將節點插入到另一個節點前面
function insertBefore(insertNode, node) {
  node.parentNode.insertBefore(insertNode, node)
}
// 將節點插入到另一個節點后面
function insertAfter(insertNode, node) {
  if (node.nextElementSibling) {
    insertBefore(insertNode, node.nextElementSibling)
  } else {
    node.parentNode.appendChild(insertNode)
  }
}
// 當松開鼠標的時候,觸發該事件。目標對象是被拖動的對象
function handleDragEnd(ev) {
  target = null
}

這樣子,我們就實現了一個可以通過拖曳來改變圖片順序的一個小玩意啦~完整的代碼放到https://github.com/chenjigeng/something 上了~有興趣的可以git clone下來跑一跑

如果覺得有用的話,麻煩點個贊哦。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現的。 ??事件,就是文檔或瀏覽器窗口中發生的一些特...
    霜天曉閱讀 3,526評論 1 11
  • html5出現了很多比較好的應用,今天我們來講講關于元素拖動的。 前言 關于拖動,我們應該理解什么是拖動源和放置目...
    儂姝沁兒閱讀 1,282評論 0 5
  • HTML標簽解釋大全 一、HTML標記 標簽:!DOCTYPE 說明:指定了 HTML 文檔遵循的文檔類型定義(D...
    米塔塔閱讀 3,310評論 1 41
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數據庫組件 SD...
    陽明AGI閱讀 16,003評論 3 119
  • 整理/陽妹兒 1.吸入中毒者,應迅速將患者移至空氣新鮮處,保暖休息. 2.口服中毒者應用0.005的活性炭懸液或0...
    陽妹兒閱讀 728評論 0 0