CSS&JS/⚡Thinkers

[JS]부모에만 mousedown,mouseup,mousemove를 적용하고 자식에는 click만 적용하고 싶을 때

arancia_ 2023. 8. 31. 14:59

JS의 .animate() 속성을 이용해서 클릭&드래그로 좌우로 이동하는 DOM을 만들건데,
1. 자식요소들을 감싸고 있는 부모만 움직임
2. 자식요소엔 클릭이벤트만 적용 (클릭시 크기가 줄어들었다/원래크기로 돌아옴)
3. 부모가 움직이고 난 후에도 자식 위에 마우스 커서가 있을때 원래는 클릭이벤트로 간주되지만, 이를 원치않음. 따라서 부모가 움직였다면 마우스 커서가 자식 위에 있더라도 클릭이벤트를 처리하지 않음.
4. 부모를 움직일때도 일정 거리 이상 마우스 커서를 움직이지 않으면, 부모도 움직이지 않게 할 것임. (이렇게 하지 않으면 아주 조그만 움직임에도 부모는 움찔거리며 계속 움직임)

 

See the Pen Untitled by Oh Ikmyeong (@dpffpself) on CodePen.

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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>mousedown, click</title>
    <link rel="stylesheet" href="./style.css">
    <script src="./main.js" type="module"></script>
</head>
<body>
    <ul id="list">
        <li class="item">1</li>
        <li class="item">2</li>
        <li class="item">3</li>
        <li class="item">4</li>
        <li class="item">5</li>
        <li class="item">6</li>
        <li class="item">7</li>
        <li class="item">8</li>
        <li class="item">9</li>
        <li class="item">10</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
@charset "utf-8";
*{margin:0;padding:0;box-sizing:border-box;}
li{list-style-type:none;}
body{
    overflow:hidden;
    display:flex;flex-flow:column nowrap;
    justify-content:center; align-items:flex-start;
    min-height:100vh;
    background:#eee;
}
#list{
    display:flex;flex-flow:row nowrap;
    justify-content:flex-start; align-items:center;
    gap:20px;
    position:relative;
    padding:30px; margin:0 30px;
    background:#ccc;
    cursor:move;
}
.item{
    flex:none;
    display:flex;
    justify-content:center; align-items:center;
    position:relative;
    width:max(15vw, 200px); aspect-ratio:16/9;
    background:#fff;
    border-radius:10px;
    user-select:none;
    cursor:pointer;
    font-size:5vmin;
    transition:transform .3s;
}
.item.on{
    transform:scale(0.5);
}
cs

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
const $list = document.getElementById('list');
const $$item = document.querySelectorAll('.item');
 
const POS = {
    x : 0,
    wid : null,
}
const REMEMBER = {
    down : 0,
    up : 0
}
 
const LIMIT_MOVE = 50;
 
function on_mouse_down(e){
    // console.log('0.mouse down');
    const {clientX} = e;
    const {left,width} = $list.getBoundingClientRect();
    REMEMBER.down = clientX;
    POS.x = clientX - left;
    POS.wid = width;
    window.addEventListener('mousemove', on_mouse_move);
    window.addEventListener('mouseup', on_mouse_up, {once:true});
}//on_mouse_down
 
function on_mouse_move(e){
    // console.log('1.mouse move');
    const {clientX} = e;
    if(Math.abs(REMEMBER.down - clientX) < LIMIT_MOVE) return;
    const newX = clientX - POS.x;
    const finalX = (newX < -1 * POS.wid) ? -1 * POS.wid + LIMIT_MOVE : (newX >= window.innerWidth ? window.innerWidth - LIMIT_MOVE : newX);
    $list.animate([{
        transform : `translateX(${finalX}px)`
    }],{
        duration : 1000,
        easing : "ease-in",
        fill : "both"
    });
}//on_mouse_move
 
function on_mouse_up(e){
    // console.log('2.mouse up');
    REMEMBER.up = e.clientX;
    window.removeEventListener('mousemove', on_mouse_move);
    $list.addEventListener('mousedown',on_mouse_down, {once:true});
}//on_mouse_up
 
function on_click(e){
    const flag = Math.abs(REMEMBER.up - REMEMBER.down) < LIMIT_MOVE ? true : false
    if(!flag) return;
    console.log('📍 item click');
    e.currentTarget.classList.toggle('on');
}//on_click
 
$list.addEventListener('mousedown',on_mouse_down, {once:true});
$$item.forEach($item =>{
    $item.addEventListener('click', on_click);
});
cs

굳이 click 이벤트를 아이템마다 걸지 않고, mouse_up 쪽에 if 문으로 e.target == 아이템 이라면 처리해도 좋다.
이는 취향차...