简体   繁体   English

使用 JavaScript 在 SortableJS 可排序列表上模拟拖放

[英]Simulating drag and drop on a SortableJS sortable list using JavaScript

I'm attempting to simulate a drag and drop action on a sortable HTML list created using the Sortable library .我正在尝试在使用Sortable library创建的可排序 HTML 列表上模拟拖放操作。 It uses the native HTML5 API to implement draggable elements and sorting within a list.它使用原生 HTML5 API 来实现可拖动元素和列表内的排序。

To simulate these drag events, I found and modified the following JavaScript code:为了模拟这些拖动事件,我找到并修改了以下 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;
};

And the markup of the section I'm trying to drag and drop with is as follows:我试图拖放的部分的标记如下:

在此处输入图片说明

When I tried to set breakpoints on the browser's drag event listeners, and execute the helper function in my browser console using:当我尝试在浏览器的拖动事件侦听器上设置断点,并使用以下命令在浏览器控制台中执行辅助函数时:

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

I noticed that the dragstart event was never captured, but the mousedown and dragover events were.我注意到 dragstart 事件从未被捕获,但 mousedown 和 dragover 事件被捕获。

How can I get the dragstart event fire to trigger its listener?如何触发 dragstart 事件以触发其侦听器? Because I think that is what's causing the drag and drop simulation to fail.因为我认为这就是导致拖放模拟失败的原因。

I can see in your code the dragstart event is created of type MouseEvent while it is of type DragEvent .我可以在您的代码中看到dragstart事件创建的类型为MouseEvent而它的类型为DragEvent

 var elem = document.getElementById("one"); var mousedown = document.createEvent('MouseEvent'); mousedown.initMouseEvent("mousedown", true, true, window, 1, 1, 1, 0, 0, false, false, false, false, 0, elem); elem.dispatchEvent(mousedown); var dragstart = document.createEvent('DragEvent'); dragstart.initMouseEvent("dragstart", true, true, window, 1, 1, 1, 0, 0, false, false, false, false, 0, elem); elem.dispatchEvent(dragstart);
 <div id="one" draggable="true" onmousedown="console.log('mousedown')" ondragstart="console.log('dragstart')">drag me</div>

Creating the dragstart as an event of the correct type, at least on Chrome and Edge, it works.dragstart创建为正确类型的事件,至少在 Chrome 和 Edge 上,它是有效的。

Hope this helps.希望这可以帮助。

I used a more modern approach to trigger the dragstart and mousedown events (note that using event constructors is preferable over document.createEvent() ).我使用了一种更现代的方法来触发dragstartmousedown事件(请注意, 使用事件构造函数比document.createEvent()更可取)。 Both events work as expected.这两个事件都按预期工作。 Some code to illustrate that below:一些代码来说明如下:

 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'>

However there is a big unknown in your code;但是,您的代码中有一个很大的未知数; How does the library you are using handle these events?您使用的库如何处理这些事件? Does it even handle the ondragstart event?它甚至处理ondragstart事件吗? It may very well only use other events, thus you can't assume these events in your JS code.它很可能只使用其他事件,因此您不能在 JS 代码中假设这些事件。 The main issue is the coupling between your JS and that in the library.主要问题是您的 JS 与库中的 JS 之间的耦合。

So there are 2 ways to go about this所以有两种方法可以解决这个问题

  1. Ensure your code uses the events the library responds to (keep in mind that the code in the library and the available events may change over time)确保您的代码使用库响应的事件(请记住,库中的代码和可用事件可能会随时间变化)
  2. Implement your own drag-and-drop behaviour实现您自己的拖放行为

PS: If you are writing tests, you may decide not to test the library and trust it to behave as expected. PS:如果您正在编写测试,您可能决定不测试该库并相信它会按预期运行。 Maybe you can validate whether your implementation of the library is correct instead.也许您可以验证您对库的实现是否正确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM