超軽量・VanillaなJavascriptでiPhoneなどのtouchデバイスでDrag & Drop Sort

2019-04-19

HTML5の標準でDrag&Dropが実装されましたので、jQueryなどを使わなくてもドラッグ&ドロップが簡単に実現できるようになりました。 ただし、マウスイベントのみという状況なので、iPhoneなどのマウスを使わない、Touchイベントのデバイスでは一工夫必要です。

実際に動くデモを見てもらったほうが早いので、iPhoneなどのTouchデバイスで以下のリストをドラッグして並び替えてみてください。

2019/4/19修正:タッチしている座標を取得するのにe.changedTouches[0].pageXとすべきところがe.pageXとなってました。これを公開した2015年当時はちゃんとテストしたと思うけど、動いてたのかなぁ?

Drag and Drop sort デモ

  • One
  • Two
  • Three

JavaScriptのコードはたったこれだけです。
もちろんjQueryなどのライブラリは一切使用しません。

デモではPCでもドラッグできるように、dragenterなどのイベントも設定していますが、基本的には、必要なのは以下のコードのみです。

JavaScriptコード + HTML

<style>
   #demo{
      border:1px solid #333;
   }
   #demo li{
      margin:10px;
      padding:10px;
      background:#aaa;
   }
</style>
<div id="demo">
   <ul>
      <li>One</li>
      <li>Two</li>
      <li>Three</li>
   </ul>
</div>
<script>
var lis = document.querySelectorAll('li');
var draggingItem;
[].forEach.call(lis, function(li){
    li.setAttribute('draggable', true);
    li.addEventListener('touchstart', function(e){
        draggingItem = li;
    });
    li.addEventListener('touchend', function(e){
        draggingItem = null;
    });
    li.addEventListener('touchmove', function(e){
        e.preventDefault();
        var getOrder = function(elem){
            return [].indexOf.call(elem.parentNode.children, elem);
        }
        var ec = e.changedTouches[0];
        var pointedElement = document.elementFromPoint(ec.pageX - window.pageXOffset, ec.pageY - window.pageYOffset);
        if(!pointedElement.getAttribute('draggable') || pointedElement == draggingItem) return;
        var order = getOrder(pointedElement) - getOrder(draggingItem);
        if(order > 0) {
            pointedElement.parentNode.insertBefore(pointedElement, draggingItem);
        } else if (order < 0) {
            pointedElement.parentNode.insertBefore(draggingItem, pointedElement);
        }
    });
});
</script>

きもは、touchmoveイベントで設定している以下の関数で、

var pointedElement = document.elementFromPoint(ec.pageX - window.pageXOffset, ec.pageY - window.pageYOffset);
if(!pointedElement.getAttribute('draggable') || pointedElement == draggingItem) return;

これがHTML5で実装されていないtouchenterの代わりの役目をしています。

そろそろみんなjQueryから脱して、Vanillaなコードを広めていきましょうよ。

JavaScriptHTML5