web-dev-qa-db-ja.com

JavaScriptを使用したSortableJSソート可能リストでのドラッグアンドドロップのシミュレーション

Sortable library を使用して作成されたソート可能なHTMLリストでドラッグアンドドロップアクションをシミュレートしようとしています。ネイティブHTML5 APIを使用して、ドラッグ可能な要素を実装し、リスト内で並べ替えます。

これらのドラッグイベントをシミュレートするために、次のJavaScriptコードを見つけて変更しました。

var triggerSortableDragAndDrop = function (selectorDrag, selectorDrop, callback) {
  var DELAY_INTERVAL_MS = 10;
  var MAX_TRIES = 2;

  // fetch target elements
  var elemDrag = document.querySelector(selectorDrag);
  var elemDrop = document.querySelector(selectorDrop);
  elemDrag.setAttribute('draggable',"true");
  elemDrop.setAttribute('draggable',"true");
  elemDrag.href="#";
  
  var dragItems = document.querySelectorAll('[draggable=true]');


  if (!elemDrag || !elemDrop) {
    console.log("can't get elements");
    return false;
  }

  var startingDropRect = elemDrop.getBoundingClientRect();

  function rectsEqual(r1, r2) {
    return r1.top === r2.top && r1.right === r2.right && r1.bottom === r2.bottom && r1.left === r2.left;
  }

  // function for triggering mouse events
  function fireMouseEvent(type, elem) {
    var evt = document.createEvent('MouseEvent');
    evt.initMouseEvent(type, true, true, window, 1, 1, 1, 0, 0, false, false, false, false, 0, elem);
    elem.dispatchEvent(evt);
  };

  // trigger dragging process on top of drop target
  // We sometimes need to do this multiple times due to the vagaries of
  // how Sortable manages the list re-arrangement
  var counter = 0;
  function dragover() {
    counter++;
    console.log('DRAGOVER #' + counter);

    var currentDropRect = elemDrop.getBoundingClientRect();
    if (rectsEqual(startingDropRect, currentDropRect) && counter < MAX_TRIES) {
      if (counter != 1) console.log("drop target rect hasn't changed, trying again");

      // mouseover / mouseout etc events not necessary
      // dragenter / dragleave events not necessary either
      fireMouseEvent('dragover', elemDrop);

      setTimeout(dragover, DELAY_INTERVAL_MS);
    } else {
      if (rectsEqual(startingDropRect, currentDropRect)) {
        console.log("wasn't able to budge drop target after " + MAX_TRIES + " tries, aborting");
        fireMouseEvent('drop', elemDrop);
        if (callback) callback(false);
      } else {
        setTimeout(drop, DELAY_INTERVAL_MS);
      }
    }
  }

  function drop() {
    console.log('DROP');
    // release dragged element on top of drop target
    fireMouseEvent('drop', elemDrop);
    fireMouseEvent('mouseup', elemDrop);    // not strictly necessary but I like the symmetry
    if (callback) callback(true);
  }

  // start dragging process
  console.log('DRAGSTART');
  fireMouseEvent('mousedown', elemDrag);
  console.log('mousedown triggered');
  fireMouseEvent('dragstart', elemDrag);
  console.log('dragstart triggered');

  // after a delay, do the first dragover; this will run up to MAX_TRIES times
  // (with a delay between each run) and finally run drop() with a delay:
  setTimeout(dragover, DELAY_INTERVAL_MS);
  return true;
};

そして、ドラッグアンドドロップしようとしているセクションのマークアップは次のとおりです。

enter image description here

ブラウザーのドラッグイベントリスナーにブレークポイントを設定し、ブラウザーコンソールでヘルパー関数を実行しようとすると、次のようになります。

triggerSortableDragAndDrop('#bookmarkItems > li:nth-child(2)', '#bookmarkItems > li:nth-child(2)');

Dragstartイベントがキャプチャされなかったことに気付きましたが、mousedownイベントとdragoverイベントはキャプチャされました。

Dragstartイベントを発生させてそのリスナーをトリガーするにはどうすればよいですか?ドラッグアンドドロップシミュレーションが失敗する原因はそれだと思います。

11

私はdragstartおよびmousedownイベントをトリガーするために、より近代的なアプローチを使用しました( イベントコンストラクターの使用はdocument.createEvent() よりも望ましいことに注意してください)。どちらのイベントも期待どおりに機能します。以下のことを説明するいくつかのコード:

let text = document.getElementById('image');
text.addEventListener('dragstart', () => {
  console.log('dragstart triggered')
});
text.addEventListener('mousedown', () => {
  console.log('mousedown triggered')
});

function btn_click() {
  const evt_1 = new MouseEvent('mousedown');
 text.dispatchEvent(evt_1);
  
  const evt_2 = new DragEvent('dragstart');
 text.dispatchEvent(evt_2);
}
<p>Drag around the image to trigger the ondragstart and mousedown event.</p>
<button onclick='btn_click()'>Programatically trigger the ondragstart and onmousedown events.</button>
<br>
<br>
<img id='image' src='https://via.placeholder.com/150'>

ただし、コードには大きな不明点があります。使用しているライブラリはこれらのイベントをどのように処理しますか? ondragstartイベントも処理しますか?それは他のイベントのみを使用するので、JSコードでこれらのイベントを想定することはできません。主な問題は、JSとライブラリ内のJSの間の結合です。

これを行うには2つの方法があります

  1. ライブラリが応答するイベントをコードが使用していることを確認してください(ライブラリ内のコードと使用可能なイベントは時間とともに変化する可能性があることに注意してください)
  2. 独自のドラッグアンドドロップ動作を実装する

PS:テストを作成している場合は、ライブラリをテストせず、ライブラリが期待どおりに動作することを信頼することができます。代わりに、ライブラリの実装が正しいかどうかを検証できます。

0
Rob Monhemius