Drag and DropAPIを生で触る / 追従する要素を自由にする方法

# 　備考

特に面白くはない

ドラッグアンドドロップ周りのコードを書いたので備忘録件共有

----

ドラッグといえば
- DnD API
- Sortable
- Vue.Draggable

---

ドラッグといえば
- DnD API ← 生なのでめんどい
- Sortable
- Vue.Draggable

---

ドラッグといえば
- DnD API 
- Sortable ← わりとカバーできる
- Vue.Draggable

---

ドラッグといえば
- DnD API 
- Sortable 
- Vue.Draggable ← SortableのWrapper

---

![example.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122390/b5afae74-fa8e-324b-c21c-7e61d9e2d139.gif)

---

# めっちゃ便利

---

ドラッグといえば
- DnD API 

↓　ドキュメント読んで！

- Sortable 
- Vue.Draggable 

-----

# それでも生APIについて知りたい　

-----

ドラッグといえば
- DnD API ←←← これの話

-----

ドラッグといえば
- DnD API
　　- [DragEvent \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/DragEvent)　← まちがい
　　- [HTML ドラッグ＆ドロップ API \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/HTML_Drag_and_Drop_API)　 ←　なるほどわからん
　　- [Document: drag イベント \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Document/drag_event) ←　せいかい

-----

# サンプルコード（MDN）

```html
<div class="dropzone">
  <div id="draggable" draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">
    This div is draggable
  </div>
</div>
<div class="dropzone"></div>
<div class="dropzone"></div>
<div class="dropzone"></div>
```

```js
var dragged;

/* events fired on the draggable target */
document.addEventListener("drag", function(event) {

}, false);

document.addEventListener("dragstart", function(event) {
  // store a ref. on the dragged elem
  dragged = event.target;
  // make it half transparent
  event.target.style.opacity = .5;
}, false);

document.addEventListener("dragend", function(event) {
  // reset the transparency
  event.target.style.opacity = "";
}, false);

/* events fired on the drop targets */
document.addEventListener("dragover", function(event) {
  // prevent default to allow drop
  event.preventDefault();
}, false);

document.addEventListener("dragenter", function(event) {
  // highlight potential drop target when the draggable element enters it
  if (event.target.className == "dropzone") {
    event.target.style.background = "purple";
  }

}, false);

document.addEventListener("dragleave", function(event) {
  // reset background of potential drop target when the draggable element leaves it
  if (event.target.className == "dropzone") {
    event.target.style.background = "";
  }

}, false);

document.addEventListener("drop", function(event) {
  // prevent default action (open as link for some elements)
  event.preventDefault();
  // move dragged elem to the selected drop target
  if (event.target.className == "dropzone") {
    event.target.style.background = "";
    dragged.parentNode.removeChild( dragged );
    event.target.appendChild( dragged );
  }
}, false);
```


-----

|event|handler|desc|
|----|----|----|
|drag|ondrag|…ドラッグ項目 (要素や選択テキスト) がドラッグされた場合|
|dragend|ondragend|…ドラッグ操作の終了|
|dragenter|ondragenter|…ドラッグ項目が有効なドロップ対象に入った場合|
|dragexit|ondragexit (en-US)|…要素がドラッグ操作の選択対象でなくなった場合|
|dragleave|ondragleave|…ドラッグ項目が有効なドロップ対象を離れた場合|
|dragover|ondragover|…ドラッグ項目が有効なドロップ対象にドラッグされた場合、数百ミリ秒ごとに|
|dragstart|ondragstart|…ユーザーが項目をドラッグ開始した場合|
|drop|ondrop|…項目が有効なドロップ対象にドロップされた場合|

-----

HTMLの属性に dropable=true ってつける

- つけないとドラッグ可能要素にならない
- img タグはデフォルトで true
- trueをつければdivでもなんでもドラッグ可能

-----

これだけだと、ドラッグはできるが
ドロップができない

-----
drop イベントを実装する

- drop先のDOMがわかるので、自分自身を削除する
- 元のDOMから自分自身を削除する

みたいなことがMDNの例に書いてある。

まぁ、MDNが情報源でなくてもいいんだけど

-----

# dragに追従する要素はどう決まる？

-----

dragに追従する要素はどう決まる？
- 基本：draggable=true　の要素
- [DataTransfer\.setDragImage\(\) \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/DataTransfer/setDragImage) によって変更できる
 - できるが、setDragImage はdrag開始前に画面上にレンダリングされている画像 or DOMでなければならない
 - ドラッグ時にDOMを挿入したのではおそすぎる
 
----

深堀り:DataTransfer？

- （dragstart イベントが発生した要素）から半透明の画像が生成され、ドラッグ中にマウスポインターに沿って移動します。
- *このメソッドは dragstart イベントハンドラー内で呼び出す必要があります。*

----

 何が問題か？

- Vue.Draggable の `@start` イベントは、 `@dragstart` のラッパー的なやつなのでDataTransfarへアクセスすることができない

----

どうしよう？

1. `@dragstart` (Vue2なら `@dragstart.native`) する
1. 空のdivを DataTransfer.setDragImage でセットする
1. `dragover` イベントでxy座標を取得してposition:absoluteにした生DOMのtop, leftをいじる
    - css の`transform: translate(top, left)` を用いたほうが高速かも

----

あとは地道にチクチクしてください
おわり。