javascript - 在 Windows 任务栏中(纯 JavaScript)等元素上交换位置

标签 javascript html dom

我编写了这段代码来制作任何具有类 draggable 的元素可拖动。

const d = document.getElementsByClassName("draggable");

for (let i = 0; i < d.length; i++) {
  d[i].style.position = "relative";
}

function filter(e) {
  let target = e.target;

  if (!target.classList.contains("draggable")) {
    return;
  }

  target.moving = true;
  
  e.clientX ?
  (target.oldX = e.clientX,
  target.oldY = e.clientY) :
  (target.oldX = e.touches[0].clientX,
  target.oldY = e.touches[0].clientY)

  target.oldLeft = window.getComputedStyle(target).getPropertyValue('left').split('px')[0] * 1;
  target.oldTop = window.getComputedStyle(target).getPropertyValue('top').split('px')[0] * 1;

  document.onmousemove = dr;
  document.addEventListener('touchmove', dr, {passive: false})

  function dr(event) {
    event.preventDefault();

    if (!target.moving) {
      return;
    }

    event.clientX ?
    (target.distX = event.clientX - target.oldX,
    target.distY = event.clientY - target.oldY) :
    (target.distX = event.touches[0].clientX - target.oldX,
    target.distY = event.touches[0].clientY - target.oldY)

    target.style.left = target.oldLeft + target.distX + "px";
    target.style.top = target.oldTop + target.distY + "px";
  }

  function endDrag() {
    target.moving = false;
  }
  target.onmouseup = endDrag;
  target.ontouchend = endDrag;
}
document.onmousedown = filter;
document.ontouchstart = filter;
div {
  width: 100px;
  height: 100px;
  background: red;
}
<div class="draggable"></div>

tl; dr-我想制作一个 Windows 任务栏,其中元素可以移动,其他元素根据拖动元素接近它的位置向右或向左移动。
我希望可拖动元素捕捉到网格,类似于拖动 Windows 任务栏上的图标或浏览器中另一个选项卡上的选项卡时发生的情况。
以下是我的尝试。我删除了沿垂直轴的移动和触摸支持,以使代码更具可读性。捕捉工作正常,但悬停的元素没有移动到其他空间。

const d = document.getElementsByClassName("draggable");

let grid = 50;

for (let i = 0; i < d.length; i++) {
  d[i].style.position = "relative";
  d[i].onmousedown = filter;
}

function filter(e) {
  let target = e.target;

  target.moving = true;
  target.oldX = e.clientX;

  target.oldLeft = window.getComputedStyle(target).getPropertyValue('left').split('px')[0] * 1;

  document.onmousemove = dr;

  function dr(event) {
    event.preventDefault();

    if (!target.moving) {
      return;
    }

    target.distX = event.clientX - target.oldX;
    
    target.style.left = target.oldLeft + Math.round(target.distX / grid) * grid + 'px'
  }

  function endDrag() {
    target.moving = false;
  }
  document.onmouseup = endDrag;
}
.parent {
  width: 100%;
  height: 100%;
  background: lime;
  display: flex;
  align-items: center;
}

.child {
  width: 50px;
  height: 50px;
  position: relative;
}

.one {
  background: red;
}

.two {
  background: blue;
}
<div class="parent">
  <div class="child one draggable"></div>
  <div class="child two draggable"></div>
</div>

此外,我认为检查鼠标何时越过 div 宽度的一半,当一个元素被拖动时,div应该向左或向右移动一个单位,具体取决于元素是在被拖动元素的左侧还是右侧。检查部分没有问题。我们可以比较元素的大小 offsetLeft .但是如何使元素移动?
请尝试用 Vanilla javascript 回答。

编辑:1.更新代码 2.更新标题 3.更新 tl;dr 并更改标题 3.添加更多标签

最佳答案

方法一:纯JS代码继续提问
以下代码满足问题的需求。我在原始代码中添加了更多元素(更多元素 = 更有趣)。我还在代码中添加了注释。我相信你只要阅读它就会理解代码。无论如何,这里是一个简短的解释。
捕捉 target在一个网格中
我们首先需要捕捉网格中的元素。我们先拍target ,请参阅下一部分以捕捉其他元素。以像素为单位的网格宽度在 let grid = ... 行中指定。 .对于流畅的动画,我们需要 target在我们结束拖动时捕捉,而不是在拖动时捕捉。函数endDrag中的这行代码捕捉 target拖动结束时进入网格。

target.style.left = target.oldLeft + Math.round(target.distX / grid) * grid + "px";
根据 target 移动其他元素
我们还需要移动位置为target的元素需要。否则,它们会重叠。函数moveElementAt做这项工作。这就是 moveElementAt 中发生的情况。 .
  • 我们将任何与 target 冲突的元素命名为的左上角elementAt . JavaScript 属性 .elementFromPoint检查。
  • 在检查中,我们排除了 target通过设置其 CSS pointer-eventsnone .如果 elementAt 我们什么都不做是父元素。
  • 我们检查元素是否接近 elementAt通过一些数学逻辑从左或右。
  • 如果 target来自右边,elementAt移动 grid单位向右。
  • 如果 target来自左边elementAt移动 grid单位离开。


  • const d = document.getElementsByClassName("draggable");
    
    let grid = 50; //Width of one grid box
    
    for (let i = 0; i < d.length; i++) {
      d[i].style.position = "relative";
    }
    
    function filter(e) {
      let target = e.target;
    
      target.moving = true;
      target.oldX = e.clientX;
    
      target.oldLeft =
        window
        .getComputedStyle(target)
        .getPropertyValue("left")
        .split("px")[0] * 1; //Get left style as a number
    
      document.onmousemove = dr;
    
      function dr(event) {
        event.preventDefault();
    
        if (!target.moving) {
          return;
        }
        target.distX = event.clientX - target.oldX;
        target.style.left = target.oldLeft + target.distX + "px";
        target.style.pointerEvents = "none"; //Stops target from being elementAt
        moveElementAt();
      }
    
      function endDrag() {
        target.moving = false;
        target.style.left =
          target.oldLeft + Math.round(target.distX / grid) * grid + "px";
        moveElementAt(); //Do it at endDrag() also to stop elements from overlapping
        target.style.pointerEvents = "auto";
      }
    
      function moveElementAt() {
        let rootEl = target.parentNode;
        let elementAt = document.elementFromPoint(
          target.offsetLeft,
          target.offsetTop //Get element at target's coordinates
        );
    
        if (elementAt === rootEl) {
          return
        } //Stop rootEl from moving
    
        //Move elementAt either grid units left or right depending on which way target is approaching it from
        if (target.offsetLeft - elementAt.offsetLeft * 1 <= grid / 2) //Can also compare to 0, comparing to grid/2 stops elements' position from breaking when moving very fast to some extent
        {
          elementAt.style.left =
            window
            .getComputedStyle(elementAt)
            .getPropertyValue("left")
            .split("px")[0] * 1 - grid + "px";
        } else {
          elementAt.style.left =
            window
            .getComputedStyle(elementAt)
            .getPropertyValue("left")
            .split("px")[0] * 1 + grid + "px";
        }
    
      }
      document.onmouseup = endDrag;
    }
    document.onmousedown = filter;
    .parent {
      width: 100%;
      height: 100%;
      background: lime;
      display: flex;
      align-items: center;
    }
    
    .child {
      width: 50px;
      height: 50px;
      position: relative;
    }
    
    .one {
      background: red;
    }
    
    .two {
      background: blue;
    }
    
    .three {
      background: brown;
    }
    
    .four {
      background: pink;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="utf-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
    
      <title>Hello!</title>
      <link rel="stylesheet" href="/style.css" />
      <script src="/scriptmain.js"></script>
    </head>
    
    <body>
      <div class="parent" id="parent">
        <div class="child one draggable"></div>
        <div class="child two draggable"></div>
        <div class="child three draggable"></div>
        <div class="child four draggable"></div>
      </div>
    </body>
    
    </html>

    当拖动异常快时,代码有点小故障。不过,我稍后会修复故障。
    方法 2:使用拖放 API/库
    Jon Nezbit 通知我有一个名为 SortableJs 的库。专门为此目的。针对纯 JS 解决方案提出的问题。所以我编写了一个使用拖放 API 的方法。这是一个片段。

    function sortable(rootEl) {
      let dragEl;
      for (let i = 0; i < rootEl.children.length; i++) {
        rootEl.children[i].draggable = true;
      }
    
      rootEl.ondragstart = evt => {
        dragEl = evt.target;
        rootEl.addEventListener("dragover", onDragOver);
        rootEl.addEventListener("dragend", onDragEnd);
      };
    
      function onDragOver(evt) {
        let target = evt.target;
        rootEl.insertBefore(
          dragEl,
          rootEl.children[0] === target ?
          rootEl.children[0] :
          target.nextSibling || target
        );
      }
    
      function onDragEnd(evt) {
        evt.preventDefault();
    
        rootEl.removeEventListener("dragover", onDragOver);
        rootEl.removeEventListener("dragend", onDragEnd);
      }
    }
    sortable(document.getElementById("parent"))
    .parent {
      width: 100%;
      height: 100%;
      background: lime;
      display: flex;
      align-items: center;
    }
    
    .child {
      width: 50px;
      height: 50px;
      position: relative;
    }
    
    .one {
      background: red;
    }
    
    .two {
      background: blue;
    }
    
    .three {
      background: brown;
    }
    
    .four {
      background: pink;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="utf-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
    
      <title>Hello!</title>
      <link rel="stylesheet" href="/style.css" />
    </head>
    
    <body>
      <div class="parent" id="parent">
        <div class="child one draggable"></div>
        <div class="child two draggable"></div>
        <div class="child three draggable"></div>
        <div class="child four draggable"></div>
      </div>
    
      <script src="/script-dndmain.js"></script>
    </body>
    
    </html>

    该库使用 HTML Drag and Drop API,它没有给我想要的结果。但你绝对应该检查一下。另外,请查看 this该库作者的优秀文章解释了(使用纯 js)他们是如何制作该库的。虽然我没有使用它,但我相信有人会得到帮助。

    关于javascript - 在 Windows 任务栏中(纯 JavaScript)等元素上交换位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63525346/

    相关文章:

    html - 如何在 div 位置固定后没有边距顶部或顶部提及 div 位置?

    java - 生成的 XML 中存在意外属性

    javascript - 如何获得隐藏图像的高度?

    php - 结合PHP和MYSQL使用javascript函数和变量

    html - 在动态网格中排列 Div

    javascript - 保存父页面时,将输入类型=文件保存在 Iframe 中

    javascript - 如何检测页面上是否未使用 CSS @font-face?

    javascript - 将表单输入设置为 firebase 中的值

    javascript - 如何创建下拉汉堡菜单

    javascript - 如何使用带有源文件符号路径的 ES6 导入功能?