티스토리 뷰
이번에는 버튼을 클릭하면 버튼의 모양을 바꾸게 할 것이다. 기존의 햄버거 모양에서 X로, X에서 다시 햄버거 모양으로 바뀌게 할 것이다. 이를 위해 이번에는 자바스크립트 코드를 작성할 것이다. 시작 전, 기본 HTML 코드는 아래와 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<main>
<button type="button" class="hamburger" id="hamburger_button">
<span class="button_text">햄버거 버튼을 위한 선</span>
</button>
</main>
<script src="./script.js"></script>
</body>
</html>
버튼에 hamburger_button이라는 id를 추가하였다. 그리고 저번 글에는 button의 class에 clicked가 있었다. 하지만 clicked는 버튼이 클릭된 경우 추가될 클래스이며 버튼은 처음에는 클릭되지 않은 상태이기에 clicked를 제거하였다.
이제 본격적으로 코드를 작성하기 전, 대략적인 의사 코드를 구상해보자.
- 버튼 요소를 선택한다.
- 버튼 요소가 클릭되는 것을 감지한다.
- 버튼이 클릭되었을 경우, clicked 클래스를 추가/제거한다.
- 만약 버튼이 클릭되지 않은 상황에서 클릭을 한다면 clicked를 추가한다.
- 만약 버튼이 이미 클릭된 상황에서 한 번 더 클릭한다면 clicked를 제거한다.
이제 이것들을 구현하면 될 것이다.
1. 버튼 요소 선택과 클릭 감지
우선 버튼을 선택할 것이다. 이를 위해 getElementById를 이용한다.
const button = document.getElementById("hamburger_button");
console.log(button);
그리고 addEventListner를 이용하여 버튼의 클릭을 감지할 것이다. addEventListner는 특정 요소에 특정 동작이 실행될 경우, 동작할 함수를 등록하고 싶을 때 이용할 수 있다. 지금은 button이 click된 경우를 감지해야 한다. 이는 아래처럼 작성할 수 있다.
const button = document.getElementById("hamburger_button");
console.log(button);
const handleButtonClick = () => {
console.log("버튼을 클릭했습니다!");
};
button.addEventListener("click", handleButtonClick);
handleButtonClick은 버튼이 클릭되었을 경우 실행할 함수이다. 현재는 확인을 위해 임시로 console.log를 작성하였다.
버튼을 클릭했을 경우 "버튼을 클릭했습니다!"라는 문자열이 출력되는 것을 확인할 수 있다.
2. 현재 버튼의 상태 확인
버튼을 클릭한 경우, 버튼이 현재 클릭된 상태인지 아닌지를 확인해야 한다. 이는 버튼의 class에 clicked이 존재하는지 아닌지로 확인할 수 있으며, 이는 classList를 이용하면 가능하다. 그리고 이 classList에 특정 클래스가 포함되어 있는지는 contains 메서드를 이용해 확인할 수 있다.
const button = document.getElementById("hamburger_button");
const handleButtonClick = () => {
const classList = button.classList;
console.log(classList);
console.log(
`classList.contains("hamburger") => ${classList.contains("hamburger")}`
);
console.log(
`classList.contains("clicked") => ${classList.contains("clicked")}`
);
};
button.addEventListener("click", handleButtonClick);
4번째 줄에서 button의 classList를 저장하였다. 그리고 아래의 3개의 console.log를 통해 classList와 hamburger, clicked의 유무를 확인할 수 있다.
3. clicked 클래스 추가
버튼이 클릭되지 않은 상태에서 버튼을 클릭한 경우, 버튼의 classList에 clicked를 추가해야 한다. classList에 특정 클래스를 추가하기 위해서는 add 메서드를 이용하면 된다.
const button = document.getElementById("hamburger_button");
const handleButtonClick = () => {
const classList = button.classList;
classList.add("clicked");
console.log(button.classList);
};
button.addEventListener("click", handleButtonClick);
버튼이 X로 바뀌는 것을 확인할 수 있다. 그런데 여기서 button.classList가 아닌, 이를 복사한 classList에 clicked를 추가했는데 button.classList에 clicked가 추가되는 것을 확인할 수 있다. 이는 const classList = button.classList;가 얕은 복사이기에 결국 button.classList의 메모리 위치를 참조하기 때문이다. 그렇기에 이번에는 헷갈리지 않게 코드를 작성하기 위해 저 classList 대신 button.classList를 이용할 것이다.
4. clicked 클래스 제거
이제 clicked 클래스를 제거할 것이다. 그런데 clicked가 존재해야 이를 제거할 수 있다. 그렇기에 if문을 이용하여 clicked가 존재할 경우, remove를 이용하여 clicked를 제거할 것이다. remove 역시 add처럼 제거하고 싶은 클래스 이름을 인자로 작성하면 된다.
const button = document.getElementById("hamburger_button");
const handleButtonClick = () => {
if (button.classList.contains("clicked")) {
button.classList.remove("clicked");
} else {
button.classList.add("clicked");
}
console.log(button.classList);
};
button.addEventListener("click", handleButtonClick);
만약 버튼에 clicked가 없으면 추가하고 있다면 제거한다. 이를 통해 버튼을 클릭하여 모양을 변경할 수 있다. 그런데 버튼이 변하는 게 너무 부자연스럽다. 이를 해결하기 위해 애니메이션을 적용할 것이다.
5. transition
button의 before와 after에서 transform과 margin-top이 변하기에 이 둘에 transition을 적용할 것이다.
/* style.css 중 */
.hamburger::before,
.hamburger::after {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
transition-property: transform, margin-top;
transition-duration: 150ms;
transition-timing-function: ease-out;
}
두 선의 움직임 자체는 부드러워졌지만 그럼에도 뭔가 부자연스럽다. 이를 해결하기 위해 중간 단계를 추가할 것이다. 지금은 햄버거 모양에서 바로 X 모양으로 넘어간다. 하지만 중간에 一 모양을 거치게 할 것이다.
6. prev_clicked 클래스
버튼이 클릭된 경우에는 clicked 클래스가 존재하며 버튼의 모양은 X이다. 그리고 햄버거와 X 사이의 버튼에는 prev_clicked 클래스가 존재하고 버튼의 모양은 一일 것이다.
/* style.css 중 */
.hamburger.clicked::before,
.hamburger.clicked::after,
.hamburger.prev_clicked::before,
.hamburger.prev_clicked::after {
margin-top: 0;
}
이제 一에서 X로 모양을 바꿔보자. 이를 위해 우선 자바스크립트 코드를 이렇게 수정해야 한다.
const button = document.getElementById("hamburger_button");
const handleButtonClick = () => {
if (button.classList.contains("clicked")) {
// 나중에 채울 부분
} else {
const clickButton = () => {
button.removeEventListener("transitionend", clickButton);
button.classList.remove("prev_clicked");
button.classList.add("clicked");
};
button.classList.add("prev_clicked");
button.addEventListener("transitionend", clickButton);
}
};
button.addEventListener("click", handleButtonClick);
우선 코드의 흐름을 살펴보자.
- 버튼을 클릭할 경우, handleButtonClick이 실행된다.
- 그리고 button.classList.add("prev_clicked");로 인해 버튼에 prev_clicked라는 클래스가 추가된다.
- prev_clicked가 추가되어 버튼이 一 모양으로 변한다.
- 버튼의 모양이 변했기에 버튼에서 transitionend 이벤트를 감지하여 clickButton이 실행된다. (button.addEventListener("transitionend", clickButton);)
- (혹시 모르는 상황을 위해) button.removeEventListener("transitionend", clickButton);로 transitionend 이벤트를 제거한다.
- prev_clicked 클래스를 제거하고 clicked 클래스를 추가한다.
이러한 과정을 거쳐 보다 모양을 자연스럽게 변화시킬 수 있었다. 반대로 X에서 햄버거로 변경시킬 때도 비슷하게 처리할 수 있다.
const button = document.getElementById("hamburger_button");
const handleButtonClick = () => {
if (button.classList.contains("clicked")) {
// 나중에 채울 부분
const resetButton = () => {
button.removeEventListener("transitionend", resetButton);
button.classList.remove("prev_clicked");
};
button.classList.add("prev_clicked");
button.classList.remove("clicked");
button.addEventListener("transitionend", resetButton);
} else {
const clickButton = () => {
button.removeEventListener("transitionend", clickButton);
button.classList.remove("prev_clicked");
button.classList.add("clicked");
};
button.classList.add("prev_clicked");
button.addEventListener("transitionend", clickButton);
}
};
button.addEventListener("click", handleButtonClick);
다음은 클릭된 버튼을 원래대로 되돌리는 과정이다.
- 버튼의 prev_clicked 클래스를 추가한다.
- 버튼의 clicked 클래스를 제거한다.
- (hamburger 제외) 버튼의 클래스에는 clicked가 없고 prev_clicked만 존재하기에 버튼이 一 모양으로 변한다.
- 버튼의 모양이 변했기에 transitionend 이벤트를 감지하여 resetButton을 실행한다.
- resetButton에서 버튼의 prev_clicked 클래스를 제거한다.
- 버튼에는 클릭 관련 클래스가 없기에 원래의 햄버거 모양으로 돌아간다.
이러한 과정을 거쳐 클릭하면 X로 변하는 햄버거 버튼을 만들 수 있었다.
7. 전체 코드
7-1. index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<main>
<button type="button" class="hamburger" id="hamburger_button">
<span class="button_text">햄버거 버튼을 위한 선</span>
</button>
</main>
<script src="./script.js"></script>
</body>
</html>
7-2. style.css
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
menu,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
main,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section {
display: block;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[hidden] {
display: none;
}
body {
line-height: 1;
}
menu,
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
* {
box-sizing: border-box;
}
body {
font-weight: 300;
font-family: "Source Sans Pro", sans-serif;
line-height: 1.2;
}
a {
text-decoration: none;
color: inherit;
}
html {
font-size: 62.5%;
}
button {
appearance: none;
border: 0;
padding: 0;
background-color: transparent;
border-radius: 0;
}
/* 작성 시작 */
main {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.hamburger {
width: 200px;
height: 200px;
border: 1px solid black;
position: relative;
}
.button_text {
width: 0;
height: 0;
overflow: hidden;
font-size: 0;
line-height: 0;
color: rgba(0, 0, 0, 0);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.hamburger::before,
.hamburger::after,
.hamburger span {
content: "";
width: 150px;
height: 10px;
background-color: black;
border-radius: 5px;
}
.hamburger::before,
.hamburger::after {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
transition-property: transform, margin-top;
transition-duration: 150ms;
transition-timing-function: ease-out;
}
.hamburger::before {
margin-top: -50px;
}
.hamburger::after {
margin-top: 50px;
}
.hamburger.clicked span {
display: none;
}
.hamburger.clicked::before,
.hamburger.clicked::after,
.hamburger.prev_clicked::before,
.hamburger.prev_clicked::after {
margin-top: 0;
}
.hamburger.clicked::before {
transform: translate(-50%, -50%) rotateZ(45deg);
}
.hamburger.clicked::after {
transform: translate(-50%, -50%) rotateZ(-45deg);
}
7-3. script.js
const button = document.getElementById("hamburger_button");
const handleButtonClick = () => {
if (button.classList.contains("clicked")) {
const resetButton = () => {
button.removeEventListener("transitionend", resetButton);
button.classList.remove("prev_clicked");
};
button.classList.add("prev_clicked");
button.classList.remove("clicked");
button.addEventListener("transitionend", resetButton);
} else {
const clickButton = () => {
button.removeEventListener("transitionend", clickButton);
button.classList.remove("prev_clicked");
button.classList.add("clicked");
};
button.classList.add("prev_clicked");
button.addEventListener("transitionend", clickButton);
}
};
button.addEventListener("click", handleButtonClick);
'기록' 카테고리의 다른 글
강의 들은 거 요약 - Classes (0) | 2024.05.11 |
---|---|
React-Query에서 Dependency를 활용한 데이터 재요청 (0) | 2023.09.21 |
햄버거 버튼 만들기 - 2. 클릭된 버튼의 모양 (0) | 2023.09.15 |
햄버거 버튼 만들기 - 1. 기본 모양 (0) | 2023.09.14 |
Next.js에서 npm run build 시 생겼던 에러 (1) | 2023.09.03 |
- Total
- Today
- Yesterday
- 프로그래머스
- 카카오맵
- Next.js
- 순열
- 스택
- 동적계획법
- 구현
- react
- Redux
- 비트마스킹
- 자바스크립트
- 알고리즘
- react router
- BFS
- aws
- 햄버거버튼
- CSS
- 타입스크립트
- 리액트
- 다이나믹프로그래밍
- typescript
- 완전탐색
- SQL
- 백준
- themoviedb
- 넥스트js
- NextJS
- 코드스테이츠
- C++
- 브루트포스
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |