[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.)