英文:
Drag and drop not functioning on dynamically created list
问题
我有一个由JavaScript动态生成的活动列表,如下所示:
const renderList = (activities) => {
const display = document.getElementById('task-list-display');
activities.forEach((activity) => {
console.log(activity);
display.insertAdjacentHTML('beforeend', `
<li class="task-item draggable" draggable="true">
<div class="chk-descr">
<input data-a1="${activity.index}" type="checkbox" name="completed"/>
<p data-b1="${activity.index}" class="description" contenteditable="true">${activity.description}</p>
</div>
</li>
`);
});
};
我希望使其具有响应性,以便可以使用拖放进行重新排列。然而,我无法使其正常工作。以前我设计了完全相同的应用程序,但是与使用insertAdjacentHTML()
插入列表项不同,我是使用createElement()
创建每个元素,然后使用appendChild()
将其附加到相应的HTML元素中。那个应用程序上的拖放功能是正常的。我的问题是:是否有某些原因导致使用insertAdjacentHTML
动态生成的列表无法进行拖放?
以下是所有相关代码:
let activities = [];
const dragstart = (element) => {
element.classList.add('skateover');
};
const dragover = (element, e) => {
e.preventDefault();
element.classList.add('dragover');
};
const dragleave = (element) => {
element.classList.remove('dragover');
};
const drop = (element) => {
const skateover = document.querySelector('.skateover');
element.before(skateover);
repopulateList();
element.classList.remove('dragover');
};
const dragend = (element) => {
element.classList.remove('skateover');
};
const repopulateList = () => {
const listItems = document.querySelectorAll('.draggable');
emptyList();
let i = 0;
listItems.forEach((listItem) => {
listItem.setAttribute('activity', i);
i += 1;
const description = listItem.getElementsByClassName('description')[0].textContent;
const completed = listItem.getElementsByClassName('completed')[0].checked;
const index = listItem.getAttribute('activity');
inputActivity(description, completed, index);
});
};
const inputActivity = (description, completed, index) => {
activities.push({ description, completed, index: parseInt(index, 10) });
};
在HTML文件中:
<ul id="task-list-display"></ul>
英文:
I have a list of activities that is generated dynamically with javascript in the following manner:
const renderList = (activities) => {
const display = document.getElementById('task-list-display');
activities.forEach((activity) => {
console.log(activity);
display.insertAdjacentHTML('beforeend', `
<li class="task-item draggable" draggable="true">
<div class="chk-descr">
<input data-a1="${activity.index}" type="checkbox" name="completed"/>
<p data-b1="${activity.index}" class="description" contenteditable="true">${activity.description}</p>
</div>
</li>
`);
I want to have it be responsive in a way that the items can be rearranged using drag and drop. I am not able, however, to make this work. Previously I had designed the very same app but instead of having the items of the list be inserted using insertAdjacentHTML()
I was creating each element using createElement()
and then appending it to the corresponding HTML element using appendChild()
. The drag and drop functionality on that app was fully functioning. My question is: is there some reason why drag and drop might not work with a dynamically generated list using insertAdjacentHTML?
.
Here is all the relevant code:
let activities = [];
const dragstart = (element) => {
element.classList.add('skateover');
};
const dragover = (element, e) => {
e.preventDefault();
element.classList.add('dragover');
};
const dragleave = (element) => {
element.classList.remove('dragover');
};
const drop = (element) => {
const skateover = document.querySelector('.skateover');
element.before(skateover);
repopulateList();
element.classList.remove('dragover');
};
const dragend = (element) => {
element.classList.remove('skateover');
};
const repopulateList = () => {
const listItems = document.querySelectorAll('.draggable');
emptyList();
let i = 0;
listItems.forEach((listItem) => {
listItem.setAttribute('activity', i);
i += 1;
const description = listItem.getElementsByClassName('description')[0].textContent;
const completed = listItem.getElementsByClassName('completed')[0].checked;
const index = listItem.getAttribute('activity');
inputActivity(description, completed, index);
});
};
const inputActivity = (description, completed, index) => {
activities.push({ description, completed, index: parseInt(index, 10) });
};
And in the HTML file:
<ul id="task-list-display"></ul>
答案1
得分: 1
这不是问题。在这里,我将事件监听器添加到无序列表(<ul>
)中。因此,添加、克隆和移除列表项(<li>
)不是问题。
在使用诸如 insertAdjacentHTML()
这样的方法时没有问题。在这个示例中,我只是使用 cloneNode()
来克隆移动的节点,然后使用 insertBefore()
将克隆的节点插入到鼠标悬停/放置的列表项之前。
const aktivities = [{
index: 1,
description: "Item 1"
},
{
index: 2,
description: "Item 2"
},
{
index: 3,
description: "Item 3"
}
];
const display = document.getElementById('task-list-display');
const renderList = (activities) => {
activities.forEach((activity) => {
display.insertAdjacentHTML('beforeend', `
<li class="task-item draggable" draggable="true" data-id="${activity.index}">
<div class="chk-descr">
<input data-a1="${activity.index}" type="checkbox" name="completed"/>
<p data-b1="${activity.index}" class="description" contenteditable="true">${activity.description}</p>
</div>
</li>
`)
});
};
display.addEventListener("dragstart", e => {
e.dataTransfer.setData("text/plain", e.target.dataset.id);
});
display.addEventListener("dragover", e => {
e.preventDefault();
[...display.querySelectorAll('li')].forEach(li => li.classList.remove('over'));
e.target.closest('li.task-item').classList.add('over');
});
display.addEventListener("drop", e => {
e.preventDefault();
[...display.querySelectorAll('li')].forEach(li => li.classList.remove('over'));
let original = document.querySelector(`li[data-id="${e.dataTransfer.getData("text/plain")}"]`);
let clone = original.cloneNode(true);
let target = e.target.closest('li.task-item');
display.insertBefore(clone, target);
display.removeChild(original);
});
renderList(aktivities);
ul {
margin: 0;
padding: 0;
list-style: none;
}
li div {
display: flex;
flex-direction: row;
}
.over {
border-top: solid thin black;
}
<ul id="task-list-display"></ul>
英文:
That is not a problem. Here I add the eventlisteners to the unordered list (<ul>
). So, the adding, cloning and removing of list items (<li>
) is not an issue.
There is no problem in using methods like insertAdjacentHTML()
. In this example I just use cloneNode()
for cloning the node that is moved and then insertBefore()
to insert the cloned node before the list item that is hovered/dropped on.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const aktivities = [{
index: 1,
description: "Item 1"
},
{
index: 2,
description: "Item 2"
},
{
index: 3,
description: "Item 3"
}
];
const display = document.getElementById('task-list-display');
const renderList = (activities) => {
activities.forEach((activity) => {
display.insertAdjacentHTML('beforeend', `
<li class="task-item draggable" draggable="true" data-id="${activity.index}">
<div class="chk-descr">
<input data-a1="${activity.index}" type="checkbox" name="completed"/>
<p data-b1="${activity.index}" class="description" contenteditable="true">${activity.description}</p>
</div>
</li>
`)
});
};
display.addEventListener("dragstart", e => {
e.dataTransfer.setData("text/plain", e.target.dataset.id);
});
display.addEventListener("dragover", e => {
e.preventDefault();
[...display.querySelectorAll('li')].forEach(li => li.classList.remove('over'));
e.target.closest('li.task-item').classList.add('over');
});
display.addEventListener("drop", e => {
e.preventDefault();
[...display.querySelectorAll('li')].forEach(li => li.classList.remove('over'));
let original = document.querySelector(`li[data-id="${e.dataTransfer.getData("text/plain")}"]`);
let clone = original.cloneNode(true);
let target = e.target.closest('li.task-item');
display.insertBefore(clone, target);
display.removeChild(original);
});
renderList(aktivities);
<!-- language: lang-css -->
ul {
margin: 0;
padding: 0;
list-style: none;
}
li div {
display: flex;
flex-direction: row;
}
.over {
border-top: solid thin black;
}
<!-- language: lang-html -->
<ul id="task-list-display"></ul>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论