CSS&JS/⚡Thinkers

vanilla JS로 sortable list 구현하기 (3)

arancia_ 2021. 12. 9. 17:06

진정한 아코디언 형식으로 이제 쌉가능하지만, ... 다만!! 리스트가 비게 되면 더이상 추가가 되지 않기 때문에 ㅠ.ㅠ 만일 내가 옮기려는 아이템이 그 해당 sub에서 단 1개뿐이었다면 이동후 다시 취소 시킨다.

 

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>test_3</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
<script src="./main.js" type="module"></script>
</head>
<body>
이렇게 하면.. 리스트가 비었을때 ㅎ.. 더이상 안 들어가...
<ul id="myUL">
    <li>
        <div class="d1">A.나는 드래그 하면 안되양~~~</div>
        <ul class="sub">
            <li class="d2-item">001</li>
            <li class="d2-item">002</li>
            <li class="d2-item">003</li>
        </ul>
    </li>
    <li>
        <div class="d1">B.나는 드래그 하면 안되양~~~</div>
        <ul class="sub">
            <li class="d2-item">가가가</li>
            <li class="d2-item">나나나나나나나</li>
            <li class="d2-item">다ㅏㅏㅏㅏㅏㅏㅏ</li>
        </ul>
    </li>
    <li>
        <div class="d1">A.나는 드래그 하면 안되양~~~</div>
        <ul class="sub">
            <li class="d2-item">001</li>
            <li class="d2-item">002</li>
            <li class="d2-item">003</li>
        </ul>
    </li>
</ul>
 
</body>
</html>
cs

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@charset "utf-8";
*{margin:0;padding:0;box-sizing:border-box;}
html,body{width:100%;min-height:100vh;}
li{list-style-type:none;}
 
#myUL{
    position:relative;
    margin:3rem auto; padding:10px;
    width:500px;
    border:1px solid black;}
 
#myUL li{
    position:relative;
    width:100%;}
 
.d1{
    padding:1.5em 1.5em;
    border:1px solid black;
    user-select:none;}
 
.sub{
   min-height:57px; 
}
.d2-item{
    margin:10px 0; padding:.8em 1.5em;
    background:#eee;
    cursor:pointer; user-select:none;}
 
.ghost{background:rgb(93, 93, 182);color:#fff;}
cs

main.js

1
2
3
4
5
import { sortable } from "./sortable.js";
 
const myUL = document.getElementById('myUL');
 
sortable(myUL);
cs

getPosition.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// let dragIndex, dragSource
export function getMouseOffset(evt) {
    const targetRect = evt.target.getBoundingClientRect()
    const offset = {
        x: evt.pageX - targetRect.left,
        y: evt.pageY - targetRect.top
    }
    return offset
}//getMouseOffset
 
export function getElementVerticalCenter(el) {
    const rect = el.getBoundingClientRect()
    return (rect.bottom - rect.top) / 2
}//getElementVerticalCenter
cs

sortable.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import {getMouseOffset,getElementVerticalCenter} from './getPosition.js'
 
export function sortable(rootEl) {
    let dragEl;
 
    // Making all siblings movable
    const all_list_item = rootEl.getElementsByClassName('d2-item'); 
    [].slice.call(all_list_item).forEach(item =>{
        item.draggable = true;
    });
 
    // Sorting starts
    rootEl.addEventListener('dragstart'function (evt) {
        dragEl = evt.target; // Remembering an element that will be moved
 
        // Subscribing to the events at dnd
        rootEl.addEventListener('dragover', _onDragOver);
        rootEl.addEventListener('dragend', _onDragEnd);
 
        setTimeout(()=>{
            // If this action is performed without setTimeout, then
            // the moved object will be of this class.
            dragEl.classList.add('ghost');
        }, 0)
    }); //dragstart
 
    // Function responsible for sorting
    function _onDragOver(evt) {
        evt.preventDefault();
        evt.dataTransfer.dropEffect = 'move';
 
        const target = evt.target;
 
        // console.log(
        // `내가 드래그한건 ${dragEl.innerText}
        // 드래그 하며 지나치는 타겟은 ${target.innerText}`);
 
        if (target && target !== dragEl && target.classList.contains('d2-item')) {
            // Sorting
            const offset = getMouseOffset(evt)
            const middleY = getElementVerticalCenter(evt.target)
            const targetIDX = [].indexOf.call(all_list_item,target);
            if (offset.y > middleY) {
                const temp_parent = target.parentElement;
                targetIDX !== -1 && temp_parent.insertBefore(dragEl, target.nextSibling);
            } else {
                const temp_parent = target.parentElement;
                targetIDX !== -1 && temp_parent.insertBefore(dragEl, target);
            }
 
        }//if
    }//_onDragOver
 
    // End of sorting
    function _onDragEnd(evt) {
        evt.preventDefault();
        //이동 다시켰는데 sub에 아이템이 하나도 없으면
        //다음번에 추가 안되서..ㅎ..;
        const all_sub = rootEl.getElementsByClassName('sub');
        for(let sub of all_sub){
            if(sub.children.length == 0){
                sub.appendChild(dragEl);
            }//if
        }//for
 
        //이벤트 및 클래스 제거
        dragEl.classList.remove('ghost');
        rootEl.removeEventListener('dragover', _onDragOver);
        rootEl.removeEventListener('dragend', _onDragEnd);
    }//_onDragEnd
}//sortable
cs