【问题标题】:Drag'n'Drop Div return to a new final position拖放 Div 返回新的最终位置
【发布时间】:2020-06-19 04:59:42
【问题描述】:

我正在尝试创建一个拖放操作,我可以在其中将项目移动到列表上的不同位置,它会实时更新列表,所以如果在下面的示例中,我将 div 与内容一起移动1 到内容为 3 的 div 的 div 2 和 3 将向上移动,内容为 1 的 div 将代替第三个。到目前为止没问题,我可以把它放在那里,一切都是正确的。 当我不放下并将 div 移动到不可放置区域并放开它时会出现问题,在这种情况下,它将返回到我第一次将其从第一个位置拖出的原始位置,但是,我想要它回到原来 3 的新位置。

示例代码:https://codesandbox.io/s/jovial-night-phej3?fontsize=14&hidenavigation=1&theme=dark

开始拖动 1:

将鼠标悬停在 3 上,是什么让 1 取而代之

不停地拖拽移出并放下

我想让带 1 的 div 在 gree 行之后返回到新位置,而不是在 red 行之后返回到原来的位置。

我的 React 代码仅供参考

import React, { useState } from "react";
import "./App.css";

const initList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function App() {
  const [list, setList] = useState(initList);
  const [draggedItem, setDraggedItem] = useState(null);

  function onDragStartHandle(e, index) {
    setDraggedItem(list[index]);
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html", e.target.parentNode);
    e.dataTransfer.setDragImage(e.target.parentNode, 20, 22);
  }

  function onDragOverHandle(e, index) {
    e.preventDefault();

    const draggedOverItem = list[index];

    if (draggedOverItem === draggedItem) {
      return;
    }

    const items = list.filter((item) => item !== draggedItem);
    items.splice(index, 0, draggedItem);
    setList(items);
  }

  return (
    <div className="App">
      <header className="App-header">
        <h3>Drag'n'Drop</h3>
        <ul>
          {list.map((item, index) => (
            <li
              key={index}
              onDragOver={(e) => onDragOverHandle(e, index)}
              className="item-style"
            >
              <div draggable onDragStart={(e) => onDragStartHandle(e, index)}>
                {item}
              </div>
            </li>
          ))}
        </ul>
      </header>
    </div>
  );
}

export default App;

【问题讨论】:

  • 我将其复制/粘贴到一个代码框(无 App.css)中,但无法重现。也许我不太了解您的重现步骤(例如,什么是“不可拖动区域”),或者我有不同的行为,因为我没有所有您的代码。您可以尝试分享一个可以准确重现问题的代码框(或类似的)吗?
  • 谢谢,@DrewReese 我按照您的建议将代码放入了代码框
  • 好的,谢谢,我仍然无法复制。我尝试将 [1] 拖到 3 的位置并放下,这似乎可行,我也将 [1] 拖到 3 的位置不放手,我可以在它下面看到"tiles" 所有交换位置并调整,然后 继续拖动大约 任何地方 并放下,[1] 似乎正确地停留在新位置。也许这是一个浏览器问题?我在 Chrome 83 MacOS 10.14.6 上。你在用什么复制?
  • @DrewReese 感谢您的帮助,问题是当您将 1 拖到 3 时,不松手将其拖到远离列表的任何位置并松手,它会回到原来的位置,现在是数字 2(只是动画)它应该动画返回到现在的位置 3

标签: javascript html css reactjs


【解决方案1】:

好的,花了一些时间来真正研究这个问题,我能想到的最好的办法是当你拖动一个元素时,你会继续前进并更新状态,但是如果用户放弃放置,元素会“捕捉”回它来自哪里,不是它最后悬停的位置。我花了一些时间查看Drag and Drop API 并没有真正跳出能够在“飞行中”修改此行为。

我认为在技术上放弃丢弃更正确的行为,即用户不丢弃列表中的元素,是为了让列表保持它之前的状态 在开始拖动之前

这是一个解决方案,它添加了一个额外的状态来改变在拖动事件期间,如果放置成功,那么临时状态将提交到“真实”列表状态,如果不成功恢复到现有的列表状态(它只是保持不变)。

import React, { useState } from "react";
import "./App.css";

const initList = [1, 2, 3, 4, 5, 6, 7, 8, 9];

function App() {
  const [list, setList] = useState(initList);
  const [dragList, setDragList] = useState(null);
  const [draggedItem, setDraggedItem] = useState(null);

  /**
   * On drag start
   * Initialize the temporary item being dragged and drag list.
   */
  const onDragStartHandle = index => e => {
    setDraggedItem(list[index]);
    setDragList(list);
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html", e.target.parentNode);
    e.dataTransfer.setDragImage(e.target.parentNode, 20, 22);
  };

  /**
   * On drag end
   * Save updated drag list to list and nullify temporary values.
   */
  const onDragEndHandle = e => {
    e.preventDefault();
    setList(dragList);
    setDragList(null);
    setDraggedItem(null);
  };

  /**
   * On drag leave
   * Item wasn't dropped so reset temporary drag list
   */
  const onDragLeaveHandle = e => {
    e.preventDefault();
    setDragList(list);
  };

  /**
   * On drag over
   * If dragging over another item update the drag list with new position
   */
  const onDragOverHandle = index => e => {
    e.preventDefault();

    const draggedOverItem = dragList[index];

    if (draggedOverItem === draggedItem) {
      return;
    }

    const items = dragList.filter(item => item !== draggedItem);
    items.splice(index, 0, draggedItem);
    setDragList(items);
  };

  return (
    <div className="App">
      <header className="App-header">
        <h3>Drag'n'Drop</h3>
        <ul>
          {(draggedItem ? dragList : list).map((item, index) => (
            <li key={index} className="item-style">
              <div
                draggable
                onDragOver={onDragOverHandle(index)} // Moved *
                onDragLeave={onDragLeaveHandle}
                onDragEnd={onDragEndHandle}
                onDragStart={onDragStartHandle(index)}
              >
                {item}
              </div>
            </li>
          ))}
        </ul>
      </header>
    </div>
  );
}

export default App;

*li 元素移动到 div 以保留源自同一元素的所有事件。在列表项上设置 onDragOver 事件侦听器会导致一个奇怪的问题,有时 UI 会“卡”在半悬停状态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 2013-02-26
    相关资源
    最近更新 更多