HTML5 drag和drop的親手實踐

起因

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

知識儲備

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

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

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

跟拖拽有關的事件如下:

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

插入節(jié)點的方法

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

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

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

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

實踐

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

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函數(shù),當該函數(shù)觸發(fā)的時候,進行初始化操作,比如記錄當前的目標對象,拖動目標的y值,以及設置拖動的效果。

// 拖動的目標對象
let target = ''
// 拖動的目標對象的y值
let targetOffsetTop = 0
// 當元素開始被拖動時,觸發(fā)該事件,目標對象是被拖動的元素
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不會被觸發(fā),并且為了看起來更明顯,當ondragover事件觸發(fā)的時候,為目標對象增加一個dotted類。當ondragleave事件觸發(fā)的時候,則把dotted類從目標對象移除。

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

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

// 當鼠標松開被拖動元素的時候,觸發(fā)該事件。目標對象是被拖動元素懸掛的那個元素。
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')
}
// 將節(jié)點插入到另一個節(jié)點前面
function insertBefore(insertNode, node) {
  node.parentNode.insertBefore(insertNode, node)
}
// 將節(jié)點插入到另一個節(jié)點后面
function insertAfter(insertNode, node) {
  if (node.nextElementSibling) {
    insertBefore(insertNode, node.nextElementSibling)
  } else {
    node.parentNode.appendChild(insertNode)
  }
}
// 當松開鼠標的時候,觸發(fā)該事件。目標對象是被拖動的對象
function handleDragEnd(ev) {
  target = null
}

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

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

友情鏈接更多精彩內容