gpt4 book ai didi

javascript - 重新排序元素后未调用 Dragstart

转载 作者:行者123 更新时间:2023-11-30 14:43:12 31 4
gpt4 key购买 nike

我正在尝试创建一个列表,可以通过拖动其中的项目来重新排序。

当我第一次拖动元素时 dragstart.trigger="drag($event)"调用 drag(e) .在 drag(e)我设置拖动元素的数据。

拖放元素时 drop.trigger="drop($event)"调用 drop(e) .在 drop(e)我得到拖动的元素并将其从列表/父元素中删除 <ul> .之后,我将拖动的元素插入到放置的位置。

问题是一旦元素被拖动。我无法将它再次拖动到不同的目标,因为 dragstart.trigger="drag($event)"没有调用 drag(e) .

如何调用 dragstart.trigger="drag($event)"

<ul id="columns" drop.trigger="drop($event)" dragover.trigger="allowDrop($event)">
<li id="item1" class="column" draggable="true" dragstart.trigger="drag($event)" dragend.trigger="dragend($event)"><header>A</header></li>
<li id="item2" class="column" draggable="true" dragstart.trigger="drag($event)" dragend.trigger="dragend($event)"><header>B</header></li>
<li id="item3" class="column" draggable="true" dragstart.trigger="drag($event)" dragend.trigger="dragend($event)"><header>C</header></li>
<li id="item4" class="column" draggable="true" dragstart.trigger="drag($event)" dragend.trigger="dragend($event)"><header>D</header></li>
<li id="item5" class="column" draggable="true" dragstart.trigger="drag($event)" dragend.trigger="dragend($event)"><header>E</header></li>
</ul>

JS :

drag(e) {
console.log('handleDragStart');
// Target element is the source node.
this.dragSrcEl = e.currentTarget;
console.log('dragSrcEl :', this.dragSrcEl);

e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.currentTarget.outerHTML);
e.currentTarget.classList.add('dragElem');
return true;
}

allowDrop(e) {
console.log('handleDragover');
e.preventDefault();
}

dragend() {
console.log('handleDragEnd');
}

drop(e) {
console.log('handleDrop');

if (e.stopPropagation) {
e.stopPropagation();
}
// Don't do anything if dropping the same column we're dragging.
if (this.dragSrcEl != e.srcElement) {
e.currentTarget.removeChild(this.dragSrcEl);

let dropHTML = e.dataTransfer.getData('text/html');
e.srcElement.parentNode.insertAdjacentHTML('beforebegin',dropHTML)
}
e.currentTarget.classList.remove('over');
return false;
}

最佳答案

dragstart 在重新排序元素后没有被调用的原因是因为您并不是真正地重新排序它们。您实际上是在删除拖动的元素,然后插入它的新副本。

这个新副本不由 aurelia 的组合引擎处理,因此不会编译,因此 html 中的任何特定于 aurelia 的表达式都不会执行任何操作。 .trigger 此时只是一个死标签。

拖放是一种特殊的野兽,以自然方式实现从来都不是特别简单,尤其是当这些元素附加了各种自定义框架行为时。

这里有 3 个选项:

  1. 不要使用 aurelia 的 trigger,而是在首次创建它们时和创建新副本时都使用 el.addEventListener

  2. 每当您删除一个元素时,使用 aurelia 的 ViewEngine 重新编译(部分) View ,以便处理 .trigger,在后台,无论如何,确实只是 el.addEventListener

  3. 使用 repeat.for 将其转换为自定义元素,并让 Aurelia 处理 html 方面的事情。

现在选项 1 肯定是让它工作的最快方法,选项 2 会稍微更健壮和更棘手,但两者都非常 hacky。

我强烈主张使用框架而不是绕过它,因为从长远来看,事情会更容易维护,而且随着项目的发展,您可以更轻松地添加额外的奇特行为。

它看起来可能比你现在做的要复杂得多,但是通过使用更多的框架来处理低级的东西,你将拥有“活的”可拖动元素和一个你可以做很多事情的功能齐全的 Aurelia更多的东西。

所以这只是您可能如何处理选项 3 的一个示例:

在 app.js 中,将您的列变成一个 javascript 对象列表:

items = [
{ text: "A", id: "item1" },
{ text: "B", id: "item2" },
{ text: "C", id: "item3" },
{ text: "D", id: "item4" },
{ text: "E", id: "item5" }
];

在 app.html 中,将这些项目传递给 columns 自定义元素(为了使 html 与您的示例相似,我将使用 as-element)

<template>
<require from="./resources/elements/columns"></require>
<ul as-element="columns" items.bind="items"></ul>
</template>

在 resources/elements/columns.js 中,针对单个项目 View 模型而不是针对 html 元素进行操作:

import { customElement, children, bindable } from "aurelia-templating";

@customElement("columns")
export class Columns {
// keeps a list of the viewmodels of the direct "li" children
@children("li") children;

// the columns
@bindable() items;

// the currently dragged column
dragColumn;

// the customEvent we dispatch from the child "column" element
handleColDragStart(e) {
// the viewmodel we passed into the customEvent
this.dragColumn = e.detail.column;
}

allowDrop(e) {
console.log("handleDragover");
e.preventDefault();
}

drop(e) {
console.log("handleDrop");

if (e.stopPropagation) {
e.stopPropagation();
}

// source drag index
let dragIdx = this.children.indexOf(this.dragColumn);

// if we can't resolve to a sibling (e.g. dropped on or outside the list),
// naively drop it at index 0 instead
let dropIdx = 0;

// try to find the drop target
let dropTarget = e.srcElement;
while (dropTarget !== document.body) {
let dropTargetVm = dropTarget.au && dropTarget.au.controller && dropTarget.au.controller.viewModel;
if (dropTargetVm) {
dropIdx = this.children.indexOf(dropTargetVm);
break;
} else {
dropTarget = dropTarget.parentElement;
}
}

if (dragIdx !== dropIdx) {
// only modify the order in the array of javascript objects;
// the repeat.for will re-order the html for us
this.items.splice(dropIdx, 0, this.items.splice(dragIdx, 1)[0]);
}

return false;
}
}

在 resources/elements/columns.html 中,只监听我们从 column 元素发送的 customEvent,除此之外只处理 drop:

<template id="columns" drop.trigger="drop($event)" dragover.trigger="allowDrop($event)">
<require from="./column"></require>
<li as-element="column" repeat.for="col of items" column.bind="col" coldragstart.trigger="handleColDragStart($event)">
</li>
</template>

在 resource/elements/column.js 中处理 dragstartdragend 事件,然后派发一个带有对 viewModel 的引用的 customEvent(因此您不必过多地处理 html):

import { customElement, bindable } from "aurelia-templating";
import { inject } from "aurelia-dependency-injection";

@customElement("column")
@inject(Element)
export class Column {
el;
constructor(el) {
this.el = el;
}

@bindable() column;

dragstart(e) {
this.el.dispatchEvent(
new CustomEvent("coldragstart", {
bubbles: true,
detail: {
column: this
}
})
);
return true;
}
}

最后,在 resources/elements/column.html 中监听 dragstart 事件:

<template draggable="true" dragstart.trigger="dragstart($event)">
<header>${column.text}</header>
</template>

这个解决方案的部分对你来说可能看起来有点奇怪,我仍然认为有点老套的部分是我们尝试通过 el.au.controller.viewModel 获取 ViewModel >.

这是您“只需要知道”的事情。自定义元素/html 行为始终有一个 au 属性,其中包含对具有 Controller 、 View 等的行为实例的引用。

当直接针对 html 工作时,这基本上是“掌握”aurelia 的最简单(有时也是唯一)方法。对于拖放之类的事情,我认为没有任何方法可以避免这种情况,因为不幸的是没有原生的 aurelia 支持它。

关于javascript - 重新排序元素后未调用 Dragstart,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49335385/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com