최종 결과물
- 💛Logo
- 💛Logo
HTML
<body>
<div class="container">
<header>
<ul>
<li class="logo">💛Logo</li>
<li class="menus">
<ul>
<li>Home</li>
<li>About</li>
<li>Service</li>
<li>Portfolio</li>
<li>Contact</li>
</ul>
<button class="menu_close_btn"><i class="fa-sharp fa-solid fa-xmark"></i></button>
</li>
</ul>
<button class="menu_open_btn"><i class="fa-solid fa-bars"></i></button>
</header>
</div>
<script>
const header = new Header('header');
</script>
</body>
로고와 메뉴 영역으로 나뉜 간단한 구조로 작성했다.menu_close_btn
, menu_open_btn
은 css 상에서 기본적으로 숨김 처리를 해두었다.
CSS
header {
width: 100%;
border-bottom: 1px solid #fff;
display: flex;
justify-content: center;
}
header ul {
width: 100%;
max-width: 90%;
height: 60px;
display: flex;
justify-content: flex-start;
align-items: center;
}
header ul .logo {
font-weight: 700;
margin-right: auto;
}
header ul .menus {
display: flex;
justify-content: flex-start;
align-items: center;
right: 0;
}
header ul .menus li {
cursor: pointer;
color: #666;
}
header ul .menus li:hover {
color: #000;
}
header ul .menus li + li {
margin-left: 20px;
}
header .menu_close_btn,
header .menu_open_btn {
display: none;
}
header button {
border: none;
background: transparent;
font-size: 18px;
cursor: pointer;
}
@media (max-width: 768px) {
header ul .menus {
position: absolute;
top: 0;
left: 100%;
width: 80%;
height: 100%;
background: #ddd;
transition: all 0.5s ease-in-out;
}
header.open ul .menus {
left: 20%;
}
header ul .menus ul {
max-width: 100%;
height: 100%;
flex-direction: column;
justify-content: center;
}
header ul .menus li + li {
margin-left: 0;
margin-top: 10px;
}
header .menu_open_btn,
header .menu_close_btn {
display: block;
}
header .menu_close_btn {
position: absolute;
top: 20px;
right: 5%;
}
}
간단하게 flex
를 이용해서 작업을 했고,
미디어 쿼리로 가로 768px
이하에서는
버튼이 노출되고, 메뉴는 positoin:absolut;
속성을 갖게 되는데,
실제 페이지에서는 positoin:fixed;
로 하는 게 좋을 듯.
또, 버튼들이 노출된다.
JS
constructor(target)
1. target이 될 DOM의 class를 인자로 받아 this.target에 할당시킨다.
2. 할당된 target 내부의 menu_open_btn 클래스를 찾아 menuOpenBtn에 할당한다.
3. 할당된 target 내부의 menu_close_btn 클래스를 찾아 menuCloseBtn에 할당한다.
4. init() 메서드를 실행한다.
init()
1. window 객체에 resize 이벤트를 등록 후 this.resize() 매서드를 실행시킨다.
2. menuOpenBtn, menuCloseBtn에 이벤트 리스너를 등록해 각각 this.openMenu와 this.closeMenu를 등록한다.
resize()
1. 화면 너비가 768 미만일 경우 closeMenu() 매서드를 실행시킨다.
openMenu, closeMenu()
1. 각 액션에 따라 this.target의 class를 추가/제거한다.
class Header {
constructor(target) {
this.target = document.querySelector(target);
this.menuOpenBtn = this.target.querySelector('.menu_open_btn');
this.menuCloseBtn = this.target.querySelector('.menu_close_btn');
this.init();
}
init(){
window.addEventListener('resize', this.resize.bind(this));
this.menuOpenBtn.addEventListener('click', this.openMenu.bind(this));
this.menuCloseBtn.addEventListener('click', this.closeMenu.bind(this));
}
resize(){
window.innerWidth > 768 ? this.closeMenu() : null;
}
openMenu(){
this.target.classList.add('open');
}
closeMenu(){
this.target.classList.remove('open');
}
}
간단하게 header DOM 받고, 그 안에 포함된 메뉴 여는 버튼, 메뉴 닫는 버튼 불러오고
init 시키는데, 여기서 resize 이벤트를 넣어서 화면 너비가 768 이하일 때
header에 붙었던 open class를 빼준다. ( 혹시나 화면이 커졌다 작아지면 메뉴가 열려있는 걸 초기화 )
그리고 openMenu(), closeMenu()를 각각 버튼과 연결해 주면 끝이다.
이런 식으로 header도 class로 작업을 해두면 좋은 점이,
지금 이 상황에서는 별 쓸모없어 보이지만, 나중에 특정 화면마다
헤더가 다크모드/화이트모드로 바뀌어야 한다던가..
헤더의 메뉴를 어디선가 받아와서 그려줘야 한다던가..
그런 부분에서 좋다.
근데 단지 딱 지금의 버튼 클릭 기능만 있을 거면 굳이 class로 작성 안 해도 그만.
실무에서는 header에 기능이 추가되고 복잡해지면 그에 맞는 메서드들을 추가해 주면서
작업을 진행하면 좋다.