拖放:仅在正确匹配时才拖放

发布于 2025-01-10 16:57:58 字数 922 浏览 0 评论 0原文

我进行了拖放操作,它运行得非常完美。但我有不同的物品只能放置在相应的放置区中。我怎样才能实现这个目标?

function ablegenErlauben(ev) {
    ev.preventDefault();
}

function ablegen(ev) {
    ev.preventDefault();
    var data = ev.dataTransfer.getData('text');
    var target = ev.target;
    while (" "+target.className+" ".indexOf(" dropzone ") == -1) target = target.parentNode;
    target.appendChild(document.getElementById(data));
}

window.addEventListener("load",function () {
    var elms = document.querySelectorAll(".zielzone");
    for (var i = 0; i < elms.length; i++) {
        var zielzone = elms[i];
        zielzone.addEventListener("drop",ablegen);
        zielzone.addEventListener("dragover",ablegenErlauben);
    };

    elms = document.querySelectorAll("[draggable=true]")
    for (var i = 0; i < elms.length; i++) {
        var draggable = elms[i];
        draggable.addEventListener("dragstart",ziehen);
    };
});

I have this drag and drop and it runs perfect. But I have different items to drop only in their corresponding dropzones. How can I achieve this?

function ablegenErlauben(ev) {
    ev.preventDefault();
}

function ablegen(ev) {
    ev.preventDefault();
    var data = ev.dataTransfer.getData('text');
    var target = ev.target;
    while (" "+target.className+" ".indexOf(" dropzone ") == -1) target = target.parentNode;
    target.appendChild(document.getElementById(data));
}

window.addEventListener("load",function () {
    var elms = document.querySelectorAll(".zielzone");
    for (var i = 0; i < elms.length; i++) {
        var zielzone = elms[i];
        zielzone.addEventListener("drop",ablegen);
        zielzone.addEventListener("dragover",ablegenErlauben);
    };

    elms = document.querySelectorAll("[draggable=true]")
    for (var i = 0; i < elms.length; i++) {
        var draggable = elms[i];
        draggable.addEventListener("dragstart",ziehen);
    };
});

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你对谁都笑 2025-01-17 16:57:58

有几种方法可以处理这个问题,但总体来说还是要定义权限函数。在您的权限逻辑中,您可以将拖动的元素 id 与放置的元素 id 进行匹配,并执行逻辑。

在我的示例中,我创建了一个接受数组的第二个参数。在此数组中,如果容器位于拖动元素上,则可以定义容器 id;如果容器位于容器上,则可以定义拖动元素 id。然后在我的权限函数中,我将检查 array.includes(id),如果它返回 true 则允许删除它。小心拖放 API 的一些问题,例如放入 onDrop 的子元素中。

// data will take array of container id
function touchstart(e, data) {
  // store dragged element id as item
  e.dataTransfer.setData("item", e.target.id);
  // store array as string, if not provided, it store as null
  if (data != undefined) {
    e.dataTransfer.setData("data", JSON.stringify(data));
  } else {
    e.dataTransfer.setData("data", JSON.stringify(null));
  }
}

// access will be defined from container, which array of allowed element id
function drop(e, access) {
  let item = e.dataTransfer.getData("item")
  let data = JSON.parse(e.dataTransfer.getData("data"))
  let dropzone = e.target.id

  // define permission
  let permission = this.getAccess(item, dropzone, data, access)
  // prevent drop into child
  let str = dropzone.split('-')
  if (str[0] == 'el') {
    permission = false
  }
  if (permission) {
    e.target.appendChild(document.getElementById(item))
  }
}


function getAccess(item, dropzone, data, access) {
  let permission = false
  // only run if data is provided, which != null
  if (data != null) {
    /* permission = data.includes(dropzone) */
    permission = data.includes(dropzone)
  }
  // if permission already granted, just exit
  if (permission) return permission
  //if permission still false, run from container access point
  if (access != null) {
    /*  permission = access.includes(item) */
    permission = access.includes(item)

  }
  return permission
}

function dragover(event) {
  event.preventDefault();
}
.container {
  display: inline-block;
  border: 1px solid #0b79d0;
  background: #1F2227;
  min-width: 120px;
  min-height: 120px;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.el {
  color: white;
  min-width: 50px;
  min-height: 50px;
  background: #42B883;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 5px;
  margin: 5px;
}

.indicator {
  position: absolute;
  top: 5px;
  left: 5px;
  background: white;
  padding: 5px;
}
<div class="container" ondrop="drop(event,['el-3'])" id="1" ondragover="dragover(event)">
  <div class=indicator>
    container 1
  </div>
  <div id="el-1" class="el" ondragstart="touchstart(event,['1','3'])" draggable="true" ondrop="return false">
    1 and 3
  </div>
</div>

<div class="container" id="2" ondrop="drop(event,['el-3'])" ondragover="dragover(event)">
  <div class=indicator>
    container 2
  </div>
  <div id="el-2" class="el" ondragstart="touchstart(event,['2','3'])" draggable="true" ondrop="return false">
    2 and 3
  </div>
</div>

<div class="container" ondrop="drop(event)" id="3" ondragover="dragover(event)">
  <div class=indicator>
    container 3
  </div>
  <div id="el-3" class="el" ondragstart="touchstart(event,['3'])" draggable="true" ondrop="return false">
    1,2 and 3
  </div>
</div>


替代方案

为了补充我上面的答案,这里还有另一个可能实现您目标的替代方案,取自此 answer 它使用类来限制事件侦听器

const draggables = Array.from(document.querySelectorAll(".draggable"))
draggables.forEach(li => {
  li.addEventListener('dragstart', dragStart);
  //***instead of eventListener methods on all elements:***
  // li.addEventListener( 'drop'     , dragEvt   );
  // li.addEventListener( 'dragenter', dragEvt   );
  // li.addEventListener( 'dragleave', dragEvt   );
})

function dragStart() {
  const dragEl = this
  const parent = dragEl.parentElement
  const siblings = Array.from(parent.children)
  dragStartIndex = siblings.indexOf(dragEl)
  const setName = Array.from(dragEl.classList)
    .find(className => className.startsWith("set-"))
  const set = Array.from(parent.querySelectorAll("." + setName))

  //*** removing the attached listeners is easy: ***
  siblings.forEach(li => {
    li.ondragover = null
    li.ondragenter = null
    li.ondragleave = null
    li.ondrop = null
  })
  //*** attach listeners only to elements in same class: ***
  set.forEach(li => {
    li.ondragover = (e) => dragOver(e)
    li.ondragenter = (e) => dragEvt(e)
    li.ondragleave = (e) => dragEvt(e)
    li.ondrop = (e) => dragEvt(e)
  })
}

function dragOver(e) {
  e.preventDefault();
}

function dragEvt(e) {
  const parentList = e.target.closest('.draggable-list')
  const thisLI = e.target.closest('.draggable-list>li')
  const eventType = e.type
  const dragEndIndex = Array.from(parentList.children).indexOf(thisLI)
  const dragEnterIndex = Array.from(parentList.children).indexOf(thisLI)
  if (dragEnterIndex != -1) {
    if (dragEnterIndex > dragStartIndex) {
      if (eventType == "dragenter") {
        thisLI.classList.add('darken-bottom');
      } else { // if eventType is leave or drop:
        thisLI.classList.remove('darken-bottom');
      }
    } else if (dragEnterIndex < dragStartIndex) {
      if (eventType == "dragenter") {
        thisLI.classList.add('darken-top');
      } else { // if eventType is leave or drop:
        thisLI.classList.remove('darken-top');
      }
    } else if (dragEnterIndex == dragStartIndex) {
      if (eventType == "dragenter") {
        thisLI.classList.add('darken')
      } else { // if eventType is leave or drop:
        thisLI.classList.remove('darken')
      }
    }
    if (eventType == "drop") {
      moveDraggable(dragStartIndex, dragEndIndex, parentList)
    }
  }
}

function moveDraggable(fromIndex, toIndex, ul) {
  const liToMove = ul.removeChild(ul.children[fromIndex])
  ul.insertBefore(liToMove, ul.children[toIndex])
  if (fromIndex != toIndex) {
    ul.children[toIndex].classList.add("darken-plonk")
    setTimeout(() => {
      ul.children[toIndex].classList.remove("darken-plonk")
    }, 300)
  }
}
.set-1 {
  background-color: lightcoral;
}

.set-2 {
  background-color: lightgreen;
}

.set-1,
.set-2 {
  border: 1px solid gray;
  padding: 0.5rem;
  list-style: none;
  width: 10rem;
}

.darken-bottom {
  border-bottom: 3px solid #be2edd;
}

.darken-top {
  border-top: 3px solid #be2edd;
}

.darken,
.darken-bottom,
.darken-top {
  filter: brightness(90%);
  transition: all 0.6s ease-out;
}

.darken-plonk {
  filter: brightness(80%);
  border: 2px solid black;
  transition: all 1.5s ease-out;
}
<ul class="draggable-list">
  <li id="li1-1" draggable="true" class="draggable set-1">ONE</li>
  <li id="li2-1" draggable="true" class="draggable set-1">TWO</li>
  <li id="li3-1" draggable="true" class="draggable set-1">THREE</li>
  <li id="li4-2" draggable="true" class="draggable set-2">one</li>
  <li id="li5-2" draggable="true" class="draggable set-2">two</li>
  <li id="li6-2" draggable="true" class="draggable set-2">three</li>
</ul>

There's a few ways to handle this, but overall will come down to defining the permission function. In your permission logic, you can match dragged element id with dropped element id, and execute the logic.

In my example I made a 2nd param which accept an array. In this array you can define the container id if it's on drag element, and drag element id if it's on container. Then in my permission function I'll just check with array.includes(id), if it's return true then it's allowed to be dropped. Careful with some gotchas of Drag and Drop API such dropping into child element of onDrop.

// data will take array of container id
function touchstart(e, data) {
  // store dragged element id as item
  e.dataTransfer.setData("item", e.target.id);
  // store array as string, if not provided, it store as null
  if (data != undefined) {
    e.dataTransfer.setData("data", JSON.stringify(data));
  } else {
    e.dataTransfer.setData("data", JSON.stringify(null));
  }
}

// access will be defined from container, which array of allowed element id
function drop(e, access) {
  let item = e.dataTransfer.getData("item")
  let data = JSON.parse(e.dataTransfer.getData("data"))
  let dropzone = e.target.id

  // define permission
  let permission = this.getAccess(item, dropzone, data, access)
  // prevent drop into child
  let str = dropzone.split('-')
  if (str[0] == 'el') {
    permission = false
  }
  if (permission) {
    e.target.appendChild(document.getElementById(item))
  }
}


function getAccess(item, dropzone, data, access) {
  let permission = false
  // only run if data is provided, which != null
  if (data != null) {
    /* permission = data.includes(dropzone) */
    permission = data.includes(dropzone)
  }
  // if permission already granted, just exit
  if (permission) return permission
  //if permission still false, run from container access point
  if (access != null) {
    /*  permission = access.includes(item) */
    permission = access.includes(item)

  }
  return permission
}

function dragover(event) {
  event.preventDefault();
}
.container {
  display: inline-block;
  border: 1px solid #0b79d0;
  background: #1F2227;
  min-width: 120px;
  min-height: 120px;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}

.el {
  color: white;
  min-width: 50px;
  min-height: 50px;
  background: #42B883;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 5px;
  margin: 5px;
}

.indicator {
  position: absolute;
  top: 5px;
  left: 5px;
  background: white;
  padding: 5px;
}
<div class="container" ondrop="drop(event,['el-3'])" id="1" ondragover="dragover(event)">
  <div class=indicator>
    container 1
  </div>
  <div id="el-1" class="el" ondragstart="touchstart(event,['1','3'])" draggable="true" ondrop="return false">
    1 and 3
  </div>
</div>

<div class="container" id="2" ondrop="drop(event,['el-3'])" ondragover="dragover(event)">
  <div class=indicator>
    container 2
  </div>
  <div id="el-2" class="el" ondragstart="touchstart(event,['2','3'])" draggable="true" ondrop="return false">
    2 and 3
  </div>
</div>

<div class="container" ondrop="drop(event)" id="3" ondragover="dragover(event)">
  <div class=indicator>
    container 3
  </div>
  <div id="el-3" class="el" ondragstart="touchstart(event,['3'])" draggable="true" ondrop="return false">
    1,2 and 3
  </div>
</div>


Alternative

To complement my answer above, here another alternative to probably achieve your goal, taken from this answer it uses class to limit event listener

const draggables = Array.from(document.querySelectorAll(".draggable"))
draggables.forEach(li => {
  li.addEventListener('dragstart', dragStart);
  //***instead of eventListener methods on all elements:***
  // li.addEventListener( 'drop'     , dragEvt   );
  // li.addEventListener( 'dragenter', dragEvt   );
  // li.addEventListener( 'dragleave', dragEvt   );
})

function dragStart() {
  const dragEl = this
  const parent = dragEl.parentElement
  const siblings = Array.from(parent.children)
  dragStartIndex = siblings.indexOf(dragEl)
  const setName = Array.from(dragEl.classList)
    .find(className => className.startsWith("set-"))
  const set = Array.from(parent.querySelectorAll("." + setName))

  //*** removing the attached listeners is easy: ***
  siblings.forEach(li => {
    li.ondragover = null
    li.ondragenter = null
    li.ondragleave = null
    li.ondrop = null
  })
  //*** attach listeners only to elements in same class: ***
  set.forEach(li => {
    li.ondragover = (e) => dragOver(e)
    li.ondragenter = (e) => dragEvt(e)
    li.ondragleave = (e) => dragEvt(e)
    li.ondrop = (e) => dragEvt(e)
  })
}

function dragOver(e) {
  e.preventDefault();
}

function dragEvt(e) {
  const parentList = e.target.closest('.draggable-list')
  const thisLI = e.target.closest('.draggable-list>li')
  const eventType = e.type
  const dragEndIndex = Array.from(parentList.children).indexOf(thisLI)
  const dragEnterIndex = Array.from(parentList.children).indexOf(thisLI)
  if (dragEnterIndex != -1) {
    if (dragEnterIndex > dragStartIndex) {
      if (eventType == "dragenter") {
        thisLI.classList.add('darken-bottom');
      } else { // if eventType is leave or drop:
        thisLI.classList.remove('darken-bottom');
      }
    } else if (dragEnterIndex < dragStartIndex) {
      if (eventType == "dragenter") {
        thisLI.classList.add('darken-top');
      } else { // if eventType is leave or drop:
        thisLI.classList.remove('darken-top');
      }
    } else if (dragEnterIndex == dragStartIndex) {
      if (eventType == "dragenter") {
        thisLI.classList.add('darken')
      } else { // if eventType is leave or drop:
        thisLI.classList.remove('darken')
      }
    }
    if (eventType == "drop") {
      moveDraggable(dragStartIndex, dragEndIndex, parentList)
    }
  }
}

function moveDraggable(fromIndex, toIndex, ul) {
  const liToMove = ul.removeChild(ul.children[fromIndex])
  ul.insertBefore(liToMove, ul.children[toIndex])
  if (fromIndex != toIndex) {
    ul.children[toIndex].classList.add("darken-plonk")
    setTimeout(() => {
      ul.children[toIndex].classList.remove("darken-plonk")
    }, 300)
  }
}
.set-1 {
  background-color: lightcoral;
}

.set-2 {
  background-color: lightgreen;
}

.set-1,
.set-2 {
  border: 1px solid gray;
  padding: 0.5rem;
  list-style: none;
  width: 10rem;
}

.darken-bottom {
  border-bottom: 3px solid #be2edd;
}

.darken-top {
  border-top: 3px solid #be2edd;
}

.darken,
.darken-bottom,
.darken-top {
  filter: brightness(90%);
  transition: all 0.6s ease-out;
}

.darken-plonk {
  filter: brightness(80%);
  border: 2px solid black;
  transition: all 1.5s ease-out;
}
<ul class="draggable-list">
  <li id="li1-1" draggable="true" class="draggable set-1">ONE</li>
  <li id="li2-1" draggable="true" class="draggable set-1">TWO</li>
  <li id="li3-1" draggable="true" class="draggable set-1">THREE</li>
  <li id="li4-2" draggable="true" class="draggable set-2">one</li>
  <li id="li5-2" draggable="true" class="draggable set-2">two</li>
  <li id="li6-2" draggable="true" class="draggable set-2">three</li>
</ul>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文