(3)에서는 진정한 depth 개념의 아코디언으로 구현은 하였지만,
depth1의 sub이 아이템이 0개이면 다음번에 추가가 안되는 맹점이 있어서
강제로 JS에서 만일 옮기고 난 뒤 아이템이 0개라면 그 드래그를 취소하도록 짰다.
이번에는 아이템이 0개라도 다시 추가 가능하도록 했다.
간단한 CSS 트릭...
++
드래그시 발생하는 고스트 이미지도 지우고 커스텀으로 바꿨다.
3만 5천번 강조해도 모자란 Codepen@Tino Thamjarat님의 원본 코드
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
42
43
44
45
46
47
48
49
50
|
<!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.2</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">
<input type="checkbox" />
<span>키킬 001</span>
</li>
<li class="d2-item">
<input type="checkbox" />
<span>키킬 002</span>
</li>
<li class="d2-item">
<input type="checkbox" />
<span>키킬 003</span>
</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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
@charset "utf-8";
*{margin:0;padding:0;box-sizing:border-box;}
html,body{width:100%;min-height:100vh; background:#666;}
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;
background:rgb(247, 169, 169);
border:1px solid black;
user-select:none;}
.sub{
position:relative;
/* min-height:57px; */
}
.d2-item{
position:relative;
margin:10px 0; padding:.8em 1.5em;
background:#eee;
cursor:pointer; user-select:none;}
.d2-item.dontDrag{
width:100%;height:10px;
margin:0;padding:0;
background:black;
opacity:0;
}
.ghost{background:rgb(93, 93, 182);color:#fff;}
.fake{
position:fixed;
top:0;left:0;
width:478px;
padding:.8em 1.5em;
background:#eee;
opacity:.8;
transition:opacity .2s;
pointer-events:none;user-select:none;
}
|
cs |
fake.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
|
export function make_fake(dragEl){
const fake = document.createElement('DIV');
fake.classList.add('fake');
fake.style.opacity = 0;
fake.innerHTML = dragEl.innerHTML;
document.body.appendChild(fake);
setTimeout(()=>{fake.style.opacity = 0.8;},100);
}
export function move_fake(evt){
const {clientX, clientY} = evt;
const fake = document.getElementsByClassName('fake')[0];
const domX = evt.target.getBoundingClientRect().left;
const finalX = clientX - (fake.offsetWidth / 2);
const finalY = clientY - (fake.offsetHeight / 2);
fake.style.transform = `translate(${finalX}px, ${finalY}px)`;
}
export function delete_fake(){
const fake = document.getElementsByClassName('fake')[0];
document.body.removeChild(fake);
}
|
cs |
getPostion.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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
import { delete_fake, make_fake, move_fake } from './fake.js';
import {getMouseOffset,getElementVerticalCenter} from './getPosition.js'
/* 원본 코드는 https://codepen.io/vtno/pen/MXmpoy */
const fakeGhost = new Image();
fakeGhost.src = './transparent.png';
export function sortable(rootEl) {
let dragEl;
// Add ghost item 왜냐면 하나만 남았을때 더 추가 안 되는 상황 막으려고
const all_sub = rootEl.querySelectorAll('.sub');
all_sub.forEach(sub =>{
const li = document.createElement('LI');
li.classList.add('d2-item');
li.classList.add('dontDrag');
sub.appendChild(li);
});
// Making all siblings movable
const all_list_item = rootEl.getElementsByClassName('d2-item');
[].slice.call(all_list_item).forEach(item =>{
if(!item.classList.contains('dontDrag')){
item.draggable = true;
}
});
// Sorting starts
rootEl.addEventListener('dragstart', function (evt) {
dragEl = evt.target; // Remembering an element that will be moved
//fake 만들기
make_fake(dragEl);
//고스트 이미지 지우기
evt.dataTransfer.setDragImage(fakeGhost,0,0);
// 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;
// `내가 드래그한건 ${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
//fake 움직이기
move_fake(evt);
}//_onDragOver
// End of sorting
function _onDragEnd(evt) {
evt.preventDefault();
//이벤트 및 클래스 제거
dragEl.classList.remove('ghost');
rootEl.removeEventListener('dragover', _onDragOver);
rootEl.removeEventListener('dragend', _onDragEnd);
//fake 제거
delete_fake();
}//_onDragEnd
}//sortable
|
cs |
소스코드 파일
'CSS&JS > ⚡Thinkers' 카테고리의 다른 글
[JS] 바닐라 자바스크립트로 간단한 계산기 만들어보기 (0) | 2022.01.03 |
---|---|
[JS + CSS] transform을 이용한 초간단 상단 progress bar 만들기 (0) | 2021.12.14 |
vanilla JS로 sortable list 구현하기 (3) (0) | 2021.12.09 |
vanilla JS로 sortable list 구현하기 (2) (0) | 2021.12.09 |
vanilla js로 sortable list 구현하기 (0) | 2021.12.09 |