みかづきブログ・カスタム

基本的にはちょちょいのほいです。

iOS15のSafariでDOMをDrag and Dropできるようにする 👆

ことの発端

blog.kimizuka.org

かつて、Drag and Drop APIを試したことがありましたが、最近になってiPadで動作していないことに気がつきました。(iOS 15.6にて確認)

iOS 15.6で動作しなかったDEMO(Drag and Drop API)

caniuse.com

Can I use では、iOS15から対応しているのにおかしいなと思い、もろもろ試してみたところ、draggable="true"にしている要素を、

  • aタグにする
  • href属性に値を入れる

の2点を満たしたところ、無事に動作するようになりました。(なんなら、draggable="true"を外しても、href属性に値が入ったaタグはドラッグ可能でした)

iOS 15.6で動作したDEMO(aタグ)

しかし、ドラッグ可能にはしたいもののリンクにはしたくないケースも多々あるでしょう。
そこで、iOSでもaタグ以外をドラッグ可能にする方法をもろもろ調査したことがことの発端です。


解決策(mobile-drag-drop)を使う

www.npmjs.com

iOS 15.6で動作したDEMO(mobile-drag-drop)

※ 動作しない場合は「Edit in JSFiddle」を押下してJSFiddle上でご確認ください

ソースコード

JavaScript
const usePolyfill = MobileDragDrop.polyfill({
  dragImageTranslateOverride: MobileDragDrop.scrollBehaviourDragImageTranslateOverride,
});

if (usePolyfill) {
  document.addEventListener('dragenter', function(evt) {
    evt.preventDefault();
  });

  window.addEventListener('touchmove', function() {}, {
    passive: false
  });
}

const debug = document.getElementById('debug');
const effect = document.getElementById('effect');

document.getElementById('drag').addEventListener('dragstart', (evt) => {
  debug.innerText = 'dragstart';
  console.log(debug.innerText);
});

document.getElementById('drag').addEventListener('drag', (evt) => {
  debug.innerText = 'drag';
  console.log(debug.innerText);
});

document.getElementById('drag').addEventListener('dragenter', (evt) => {
  evt.preventDefault();
  debug.innerText = 'dragenter';
  console.log(debug.innerText);
});

document.getElementById('drag').addEventListener('dragend', (evt) => {
  debug.innerText = 'dragend';
  effect.innerText = evt.dataTransfer.dropEffect;

  console.log(debug.innerText);
});

document.getElementById('drop').addEventListener('dragover', (evt) => {
  evt.preventDefault();
  debug.innerText = 'dragover';
  console.log(debug.innerText);
});

document.getElementById('drop').addEventListener('dragleave', () => {
  debug.innerText = 'dragleave';
  console.log(debug.innerText);
});

document.getElementById('drop').addEventListener('drop', (evt) => {
  evt.preventDefault();
  debug.innerText = 'drop';
  console.log(debug.innerText);
});

HTML
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mobile-drag-drop@3.0.0-beta.0/default.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/mobile-drag-drop@3.0.0-beta.0/icons.css" />
<script src="https://cdn.jsdelivr.net/npm/mobile-drag-drop@2.3.0-rc.2/index.js"></script>
<p id="debug"></p>
<p id="effect"></p>
<div id="drag" draggable="true">drag</div>
<div id="drop">drop</div>

SCSS
#debug {
  position: fixed;
  top: 8px; left: 8px;
  font-size: 10px;
  
  &:before {
    content: 'eventName: ';
  }
}

#effect {
  position: fixed;
  top: 24px; left: 8px;
  font-size: 10px;
  
  &:before {
    content: 'dropEffect: ';
  }
}

div {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  top: 0; bottom: 0;
  left: 0; right: 0;
  margin: auto;
  width: 80px; height: 80px;
  color: white;
}

#drag {
  background: red;
  cursor: pointer;
  transform: translateX(-80px);
}

#drop {
  background: blue;
  transform: translateX(80px);
}

動作の様子

指が写っていないので分かりにくいですが、iOSで動作しています。

注意事項

mobile-drag-dropはCDNから読み込んだのですが、@3.0.0-beta.0を使うと、draggable="true"を付与した要素以下にテキストノードがあったときに動作しませんでした。(2022.8.11 現在)
CSSは@3.0.0-beta.0でOKでした。

www.jsdelivr.com