
이전에 작성한 포스팅 기반으로 2개 합침.
예전에는 스크롤 가능한 환경에선 드래그 도중에 튀는 경우가 있어서
레이어 팝업의 position값을 fixed에서 abosilute; 로 변경하였음.
24시간 쿠키 : https://aosceno.tistory.com/646
[JS]바닐라 자바스크립트로 24시간 쿠키 모달 끄기/켜기 설정하기
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 24hours modal 쿠키 제거 모달 보기 Lorem ipsum, dolor sit amet consectetur adipisicing elit. Molestias, perspiciatis! 24시간동안 보지 않기 닫
aosceno.tistory.com
드래그 + 크기 조절 모달 : https://aosceno.tistory.com/689
[JS]Draggable & Resize 모달창
비슷한 내용을 자주 올리지만 다시 짤 때마다 원리를 이해한 상태에서 더 간결해지고 있으므로... 드래그나 리사이즈를 할 때마다 해당 모달의 z-index가 가장 상위로 올라온듯하게 보이도록 수정
aosceno.tistory.com
codepen에서는 24h 쿠키 확인이 안 되긴 함
See the Pen Layer Popup:24hour cookie, Draggable 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>팝압</title>
<link rel="stylesheet" href="./css/style.css">
<link rel="stylesheet" href="./css/popup.css">
<script src="./js/main.js" type="module"></script>
</head>
<body style="min-height:150vh;">
<button id="delete_cookie">쿠키 삭제</button>
<section class="popup off" data-popupid="popup001">
<article class="popup-ctnt">
<img src="https://www.gg.go.kr/uploads/CONTENTS/site/gg/wolym26_prmp_keyvisual_pc.webp" class="popup-img"
draggable="false" />
<a href="#" target="_blank" class="popup-link" draggable="false">바로가기</a>
</article>
<article class="popup-btm">
<label class="popup-lbl">
<input class="popup-chk" type="checkbox" name="popup24h" value="popup001">
24시간동안 보지 않기
</label>
</article>
<button class="popup-close">X</button>
</section>
<section class="popup off" data-popupid="popup002">
<article class="popup-ctnt">
<img src="https://www.gg.go.kr/uploads/CONTENTS/site/gg/wolym26_prmp_keyvisual_pc.webp" class="popup-img"
draggable="false" />
<a href="#" target="_blank" class="popup-link" draggable="false">바로가기</a>
</article>
<article class="popup-btm">
<label class="popup-lbl">
<input class="popup-chk" type="checkbox" name="popup24h" value="popup002">
24시간동안 보지 않기
</label>
</article>
<button class="popup-close">X</button>
</section>
</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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
.popup {
position: absolute;
top: 0;
left: 0;
max-width: 500px;
border: 1px solid black;
}
.popup.off {
opacity: 0;
pointer-events: none;
}
.popup[data-popupid="popup001"] {
transform: translate(100px, 100px);
}
.popup[data-popupid="popup002"] {
transform: translate(200px, 200px);
}
.popup-ctnt {
position: relative;
width: 100%;
}
.popup-img {
width: 100%;
height: auto;
max-width: 500px;
user-select: none;
vertical-align: middle;
}
.popup-link {
position: absolute;
left: 50%;
bottom: 20px;
transform: translate(-50%, 0);
padding: 1em 3em;
background: #000;
color: #fff;
}
.popup-btm {
position: relative;
padding: 10px;
background: #000;
font-size: 12px;
color: #fff;
}
.popup-lbl {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 5px;
position: relative;
}
.popup-close {
position: absolute;
top: 0;
right: 0;
width: 50px;
aspect-ratio: 1/1;
background: rgba(0, 0, 0, .2);
border: 1px solid black;
border: none;
backdrop-filter: blur(10px);
color: #fff;
cursor: pointer;
}
|
cs |
main.js
|
1
2
3
|
import { LayerPopup } from "./LayerPopup.js";
new LayerPopup().init();
|
cs |
LayerPopup.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
import { Draggable } from "./Draggable.js";
/**
* 레이어 팝업
*/
export class LayerPopup {
constructor() {
this.$btnDeleteCookie = document.getElementById("delete_cookie");
}//constructor
/**
*
*/
init() {
/* 팝업 전체 */
const $$popup = document.querySelectorAll(".popup");
/* 쿠키 삭제 버튼 클릭시 */
this.$btnDeleteCookie.addEventListener("click", () => {
console.log("쿠키 삭제 렛츠고");
$$popup.forEach($popup => {
const popupid = $popup.dataset.popupid;
if (!popupid) { throw new Error("no popupid"); }
this.delete_cookie(popupid);
});
});
/* 닫기 기능 추가 */
$$popup.forEach($popup => {
const popupid = $popup.dataset.popupid;
if (!popupid) { throw new Error("no popupid"); }
const isClose = this.get_cookie(popupid);
/* 닫힌게 아니라면 */
if (!isClose) {
/* 팝업 보이게 */
$popup.classList.remove("off");
/* 닫기 버튼 클릭 이벤트 추가 */
const $close = $popup.querySelector(".popup-close");
$close.addEventListener("click", () => {
this.on_close($popup);
}, { once: true });
/* 드래그 이벤트 추가 */
new Draggable()
.set_dom($popup)
.init();
}
});
}//init
/**
*
* @param {*} $popup
*/
on_close = ($popup) => {
/* 24h 체크 */
const $check = $popup.querySelector(".popup-chk");
if ($check.checked) {
this.set_cookie({
popupid: $check.value,
value: "done",
expiredays: 1
});
}
/* 닫기 */
const $parent = $popup.parentElement;
$parent.removeChild($popup);
}//on_close
/**
*
* @param {*} param0
*/
set_cookie = ({ popupid, value, expiredays }) => {
const today = new Date();
today.setDate(today.getDate() + expiredays);
document.cookie = popupid + "=" + encodeURIComponent(value) + "; path=/; expires=" + today.toGMTString() + ";"
}//set_cookie
/**
*
*/
get_cookie = (popupid) => {
const cookieData = document.cookie;
console.log(cookieData);
if (cookieData.indexOf(`${popupid}=done`) < 0) {
console.log(`${popupid} 없음`);
return false;
}
const $popup = document.querySelector(`[data-popupid="${popupid}"]`);
if (!$popup) {
throw new Error("can't find popup");
}
console.log(`${popupid} 있음`);
this.on_close($popup);
return true;
}//get_cookie
/**
*
* @param {*} popupid
*/
delete_cookie = (popupid) => {
console.log(popupid);
const date = new Date("2099-01-01");
document.cookie = popupid + "=" + encodeURIComponent("show") + "; path=/; expires=" + date.toGMTString() + ";"
console.log(document.cookie);
}//delete_cookie
/* ===================================== */
/* ===================================== */
/* ===================================== */
}//class:LayerPopup
|
cs |
Draggable.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
export class Draggable {
constructor() {
this.$drag = null;
this.posX = null;
this.posY = null;
}
/**
*
* @param {*} $drag
* @returns
*/
set_dom($drag) {
this.$drag = $drag;
return this;
}//set_dom
/**
*
*/
init() {
this.toggle_start_events(true);
}//init
/**
*
*/
toggle_start_events = (add) => {
const method = add ? 'addEventListener' : 'removeEventListener';
this.$drag[method]("mousedown", this.on_start, { once: true });
this.$drag[method]("touchstart", this.on_start, { once: true, passive: false });
}//add_mouse_down
/**
* Mouse/Touch 이벤트에서 좌표를 추출하는 공통 함수
*/
get_coords(e) {
const result = { x: 0, y: 0 };
if (e.type.startsWith('touch')) {
result.x = e.touches[0].pageX;
result.y = e.touches[0].pageY;
} else {
result.x = e.pageX;
result.y = e.pageY;
}
return result;
}
/**
*
* @param {*} e
*/
on_start = (e) => {
const excludes = ["A", "BUTTON", "LABEL", "INPUT"];
if (excludes.includes(e.target.tagName)) {
this.toggle_start_events(true);
return;
}
// 터치 이벤트에서만 preventDefault를 실행하여 드래그에 집중하게 합니다.
if (e.type === 'touchstart') {
// e.cancelable 확인 후 실행 (안전장치)
if (e.cancelable) e.preventDefault();
}
// 터치 시 스크롤 등 기본 동작 방지
if (e.type === 'touchstart') { e.preventDefault(); }
document.body.appendChild(this.$drag);
const { x, y } = this.get_coords(e);
const { left, top } = this.$drag.getBoundingClientRect();
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
const scrollTop = window.scrollY || document.documentElement.scrollTop;
this.posX = x - (left + scrollLeft);
this.posY = y - (top + scrollTop);
// Move & End 이벤트 바인딩
window.addEventListener('mousemove', this.on_move);
window.addEventListener('touchmove', this.on_move, { passive: false });
window.addEventListener('mouseup', this.cancel, { once: true });
window.addEventListener('touchend', this.cancel, { once: true });
window.addEventListener('mouseleave', this.cancel, { once: true });
}//on_start
/**
*
* @param {*} e
*/
on_move = (e) => {
const { x, y } = this.get_coords(e);
const { width, height } = this.$drag.getBoundingClientRect();
const nowPosX = x - this.posX;
const nowPosY = y - this.posY;
const clamp = (val, min, max) => Math.max(min, Math.min(val, max));
const maxX = document.documentElement.scrollWidth - width;
const maxY = document.documentElement.scrollHeight - height;
const finalX = clamp(nowPosX, 0, maxX);
const finalY = clamp(nowPosY, 0, maxY);
this.$drag.style.transform = `translate(${finalX}px, ${finalY}px)`;
}//on_mouse_move
/**
*
* @param {*} e
*/
cancel = (e) => {
window.removeEventListener('mousemove', this.on_move);
window.removeEventListener('touchmove', this.on_move);
this.toggle_start_events(true);
}//cancel
}//Draggable
|
cs |
'CSS&JS > ⚡Thinkers' 카테고리의 다른 글
| [JS] 값 입력 받아서 JS로 svg 꺾은선 그래프 그리기 (0) | 2026.04.03 |
|---|---|
| [JS] canvas로 벚꽃잎 흩날리기 (0) | 2026.03.26 |
| [JS] canvas로 video 크로마키 하기 (0) | 2026.03.11 |
| [JS] canvas로 첨부파일 이미지로부터 평균 배경색상 추출해서 4사분면 그라디언트 (0) | 2026.03.09 |
| [JS] 첨부한 이미지 파일의 평균 색상값 구하기 (0) | 2026.03.09 |