【问题标题】:Simulating drag and drop on a SortableJS sortable list using JavaScript使用 JavaScript 在 SortableJS 可排序列表上模拟拖放
【发布时间】:2020-06-26 15:49:25
【问题描述】:

我正在尝试在使用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;
};

我尝试拖放的部分的标记如下:

当我尝试在浏览器的拖动事件监听器上设置断点,并在我的浏览器控制台中执行辅助函数时,使用:

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

我注意到 dragstart 事件从未被捕获,但 mousedown 和 dragover 事件被捕获。

如何让 dragstart 事件触发以触发其侦听器?因为我认为这是导致拖放模拟失败的原因。

【问题讨论】:

  • 我遇到了类似的问题,你有没有设法解决这个问题?
  • @methuselah 不,我已经放弃了:P 如果您找到可行的解决方案,请告诉我
  • 我最终使用 CypressJS 进行拖放事件。适用于npmjs.com/package/@4tw/cypress-drag-drop
  • @methuselah 希望我有这个选项:/ 我只使用 C#/Specflow 和 IE - 谢谢
  • @AuthenticReplica 网址是公开的吗?您会考虑使用 Selenium-Python 或 Selenium_Java 的解决方案吗?

标签: javascript html selenium dom-events sortablejs


【解决方案1】:

我可以在您的代码中看到 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);
&lt;div id="one" draggable="true" onmousedown="console.log('mousedown')" ondragstart="console.log('dragstart')"&gt;drag me&lt;/div&gt;

至少在 Chrome 和 Edge 上,将 dragstart 创建为正确类型的事件,它可以工作。

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    我使用了一种更现代的方法来触发dragstartmousedown 事件(注意using event constructors is preferable over 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 之间的耦合。

    所以有两种方法可以解决这个问题

    1. 确保您的代码使用库响应的事件(请记住,库中的代码和可用事件可能会随时间而变化)
    2. 实现您自己的拖放行为

    PS:如果您正在编写测试,您可能决定不测试该库并相信它会按预期运行。也许您可以验证您的库实现是否正确。

    【讨论】:

      猜你喜欢
      • 2011-10-30
      • 2018-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-13
      • 2015-07-08
      • 1970-01-01
      相关资源
      最近更新 更多