[HTML][TypeScript] 드래그 앤 드롭 API로 요소 정렬
48366 단어 typescript
소개
이번에는 드래그 앤 드롭 API로 DOM 요소를 정렬해 보았습니다.
패키지.json
{
"dependencies": {
"autoprefixer": "^10.2.4",
"postcss": "^8.2.6",
"postcss-cli": "^8.3.1",
"ts-loader": "^8.0.17",
"tsc": "^1.20150623.0",
"typescript": "^4.2.2",
"webpack": "^5.24.3",
"webpack-cli": "^4.5.0"
}
}
기본 프로젝트
홈.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello</title>
<meta charset="utf-8">
<link rel="stylesheet" href="/css/home.page.css">
</head>
<body>
<div class="draggable_area" id="draggable_area_1">
<div class="draggable_item" draggable="true" id="draggable_element_1">
<div>
title
</div>
<div>
content
</div>
</div>
<div class="draggable_item" draggable="true" id="draggable_element_2">
<div>
title2
</div>
<div>
content2
</div>
</div>
</div>
<script src="/js/homePage.js"></script>
</body>
</html>
홈페이지.페이지.css
.draggable_area {
border: 1px solid black;
background-color: cadetblue;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 20vh;
width: 40vw;
}
.draggable_item {
border: 1px solid black;
background-color: coral;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
width: 30%;
height: 90%;
}
.dragging {
opacity: 50%;
}
.drop_over {
background-color: cornflowerblue;
}
끌어서 놓기 구현
견인
대상 요소를 드래그할 수 있도록 하려면 "드래그 가능"특성을 추가하기만 하면 됩니다.
<div draggable="true">
</div>
하지만 아무데도 떨어뜨릴 수 없기 때문에 거의 아무것도 할 수 없습니다.
하락
현재 웹 브라우저에서 사용할 수 있는 "droppable"속성이 없기 때문에 직접 구현해야 합니다.
"draggable"속성과 관련된 일부 DOM 이벤트를 사용할 수 있으므로 어떤 요소가 드래그되었고 어디에 놓였는지 알 수 있습니다.
home.page.ts
const parentElement: HTMLElement = document.getElementById('draggable_area_1') as HTMLElement;
function init() {
for(let i = 0; i < parentElement.children.length; i++) {
const targetElement = parentElement.children[i] as HTMLElement;
if(targetElement.classList != null &&
targetElement.classList.contains('draggable_item'))
{
targetElement.ondragstart = ev => handleStartDraggingEvent(ev);
targetElement.ondragover = ev => handleDraggingOverEvent(ev);
targetElement.ondrop = ev => handleDroppingEvent(ev);
}
}
}
function handleStartDraggingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
console.log("[DRAG] " + target.id);
}
function handleDraggingOverEvent(ev: DragEvent) {
// Elements prevent Drop operation by default
ev.preventDefault();
}
function handleDroppingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
console.log("[DROP]" + target.id);
ev.preventDefault();
}
init();
이제 "handleStartDraggingEvent()"에서 어떤 요소가 드래그되었는지, "handleDroppingEvent()"에서 어떤 요소가 드롭되었는지 알 수 있습니다.
그러나 그들의 정보는 연결되어 있지 않습니다.
그래서 "handleStartDraggingEvent()"에 데이터를 설정하고 "handleDroppingEvent()"에 데이터를 받습니다.
home.page.ts
...
function handleStartDraggingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
console.log("[DRAG] " + target.id);
var dataTransfer = ev.dataTransfer;
if(dataTransfer == null) {
return;
}
dataTransfer.setData("text/plain", target.id);
}
...
function handleDroppingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
console.log("[DROP]" + target.id);
const dragTargetId = ev.dataTransfer!.getData("text/plain");
ev.preventDefault();
}
...
장식 추가 및 요소 정렬
장식 추가
home.page.ts
...
function init() {
for(let i = 0; i < parentElement.children.length; i++) {
const targetElement = parentElement.children[i] as HTMLElement;
if(targetElement.classList != null &&
targetElement.classList.contains('draggable_item'))
{
targetElement.ondragstart = ev => handleStartDraggingEvent(ev);
targetElement.ondragover = ev => handleDraggingOverEvent(ev);
targetElement.ondragenter = ev => handleDraggingEnterEvent(ev);
targetElement.ondragexit = ev => handleDraggingExitEvent(ev);
targetElement.ondrop = ev => handleDroppingEvent(ev);
targetElement.ondragend = ev => handleDropEndEvent(ev);
}
}
}
...
function handleStartDraggingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
console.log("[DRAG] " + target.id);
target.classList.add('dragging');
var dataTransfer = ev.dataTransfer;
if(dataTransfer == null) {
return;
}
dataTransfer.setData("text/plain", target.id);
}
function handleDraggingEnterEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
if(target.id === ev.dataTransfer!.getData("text/plain")) {
return;
}
if(target?.classList == null) {
return;
}
target.classList.add('drop_over');
}
function handleDraggingExitEvent(ev: Event) {
const target = ev.target as HTMLElement;
if(target?.classList == null) {
return;
}
target.classList.remove('drop_over');
}
...
function handleDropEndEvent(ev: Event) {
const target = ev.target as HTMLElement;
if(target?.classList == null) {
return;
}
target.classList.remove('dragging');
if(target.classList.contains('drop_over')){
target.classList.remove('drop_over');
}
}
function handleDroppingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
if(target.classList.contains('drop_over')){
target.classList.remove('drop_over');
}
...
}
한 가지 중요한 점은 "ondragenter"와 같은 이벤트가 이 샘플에서 "draggable_item"클래스가 있는 "div"요소뿐만 아니라 해당 하위(텍스트 요소)에 의해 실행된다는 것입니다.
그리고 "classList"속성이 없습니다.
따라서 요소에 클래스를 추가하거나 제거할 때 대상에 "classList"속성이 있는지 확인해야 합니다.
요소 정렬
요소를 정렬하는 특별한 방법이 없기 때문에 목록에 요소를 추가하고 정렬할 때 parentElement의 자식 요소를 정리하고 정렬된 목록을 추가합니다.
전체 TS 코드
home.page.ts
type DragTarget = {
id: string,
element: HTMLElement,
index: number,
};
const parentElement: HTMLElement = document.getElementById('draggable_area_1') as HTMLElement;
let targets = new Array<DragTarget>();
function init() {
for(let i = 0; i < parentElement.children.length; i++) {
const targetElement = parentElement.children[i] as HTMLElement;
if(targetElement.classList != null &&
targetElement.classList.contains('draggable_item'))
{
targetElement.ondragstart = ev => handleStartDraggingEvent(ev);
targetElement.ondragover = ev => handleDraggingOverEvent(ev);
targetElement.ondragenter = ev => handleDraggingEnterEvent(ev);
targetElement.ondragexit = ev => handleDraggingExitEvent(ev);
targetElement.ondrop = ev => handleDroppingEvent(ev);
targetElement.ondragend = ev => handleDropEndEvent(ev);
targets.push({
id: targetElement.id,
element: targetElement,
index: i,
});
}
}
}
function handleStartDraggingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
console.log("[DRAG] " + target.id);
target.classList.add('dragging');
var dataTransfer = ev.dataTransfer;
if(dataTransfer == null) {
console.error('dataTransfer was null');
return;
}
dataTransfer.setData("text/plain", target.id);
}
function handleDraggingEnterEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
if(target.id === ev.dataTransfer!.getData("text/plain")) {
return;
}
if(target?.classList == null) {
console.log(target);
return;
}
target.classList.add('drop_over');
}
function handleDraggingExitEvent(ev: Event) {
const target = ev.target as HTMLElement;
if(target?.classList == null) {
return;
}
target.classList.remove('drop_over');
}
function handleDraggingOverEvent(ev: DragEvent) {
ev.preventDefault();
}
function handleDropEndEvent(ev: Event) {
const target = ev.target as HTMLElement;
if(target?.classList == null) {
return;
}
target.classList.remove('dragging');
if(target.classList.contains('drop_over')){
target.classList.remove('drop_over');
}
}
function handleDroppingEvent(ev: DragEvent) {
const target = ev.target as HTMLElement;
if(target.classList.contains('drop_over')){
target.classList.remove('drop_over');
}
console.log("[DROP]" + target.id);
const dragTargetId = ev.dataTransfer!.getData("text/plain");
const dropped = targets.find(t => t.id === target.id);
const dragged = targets.find(t => t.id == dragTargetId);
if(dropped == null ||
dragged == null) {
return;
}
const droppedIndex = dropped.index;
dropped.index = dragged.index;
dragged.index = droppedIndex;
updateElements();
ev.preventDefault();
}
function updateElements() {
for(let i = parentElement.children.length - 1; i >= 0; i--) {
parentElement.removeChild(parentElement.children[i]);
}
for(const target of targets.sort((a, b) => a.index - b.index)) {
parentElement.appendChild(target.element);
}
}
init();
자원
Reference
이 문제에 관하여([HTML][TypeScript] 드래그 앤 드롭 API로 요소 정렬), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/masanori_msl/html-typescript-sort-elements-by-drag-drop-api-2m6f텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)