일단 partners
라는 컴포넌트를 구현한 글을 쓰려다가... 생각보다 기능이 너무 쉬웠습니다.
그래서 이는 넘어가고, 드롭다운 버튼을 직접 구현한 과정을 서술해보려 합니다!
일단 제가 클론한 프로그래머스의 Footer
에 위치한 드롭다운 버튼은 다음과 같은 특징이 있었습니다.
먼저 데스크탑 화면 크기 시에는 저렇게 absolute
position으로 별도로 위치했고,
태블릿 이하 화면 크기에서는 다음과 같이 밑으로 내려가는 것으로 보아, relative
로 변화시킨 것 같았습니다.
솔직히 여기까지는 정말 쉬웠는데, 저를 당황스럽게 했던 건 다음과 같은 기능이었어요.
내용들이 다 보일 정도면, 다음과 같이 밑으로 내려가다가
이렇게 다시 위로 스크롤하면 위로 올라가는 기능이 있더라구요...
아고 머리야!😂
하지만 일단 문제가 주어졌으니, 해결은 해야겠고! 결국에는 해결해냈답니다 👏👏
따라서 html
은 다음과 같이 작성했어요!
<footer class="footer">
<section class="footer__containter">
<section class="footer__categories">
<section class="footer__category">
<h5 class="footer__category-name">서비스</h5>
<a href="https://programmers.co.kr/">개발자용 프로그래머스</a>
<a href="https://business.programmers.co.kr/">기업용 프로그래머스</a>
<a href="https://school.programmers.co.kr/">프로그래머스 스쿨</a>
</section>
<section class="footer__category">
<h5 class="footer__category-name">약관</h5>
<a href="https://programmers.co.kr/privacy">개인정보 처리방침</a>
<a href="https://programmers.co.kr/tos">이용약관</a>
</section>
<section class="footer__category">
<h5 class="footer__category-name">문의</h5>
<a href="https://programmers.co.kr/zendesk/main">
<span>FAQ/문의</span>
<i class="fas fa-external-link-alt"></i>
</a>
<a href="https://programmers.zendesk.com/hc/ko/requests/new">
<span>교육 결제, 환불 관련 문의</span>
<i class="fas fa-external-link-alt"></i>
</a>
<a href="https://programmers.zendesk.com/hc/ko/requests/new">
<span>코딩 테스트(응시자) 문의</span>
<i class="fas fa-external-link-alt"></i>
</a>
</section>
<section class="footer__category">
<h5 class="footer__category-name">고객센터</h5>
<span>기업 서비스 : 02-539-1886</span>
<span>교육 내용 관련 문의 : 02-539-1885</span>
<span>제휴 및 서비스 운영, 기타 문의 : 02-539-1882</span>
<div class="footer__operation-time">
<span>문의하기 운영시간 : 오전 9시 ~ 오후 6시 (주말 및 공휴일 휴무)</span>
<span>점심시간 : 오후 12시 ~ 오후 1시</span>
</div>
</section>
</section>
<section class="footer__dropdown">
<button class="footer__dropdown-btn">
<span>패밀리사이트</span>
<div class="footer__dropdown-btn-arrow"></div>
</button>
<ul class="footer__family-sites">
<a href="https://www.grepp.co/" class="footer__family-site">그렙</a>
<a href="https://monito.io/" class="footer__family-site">모니토</a>
<a href="https://hashcode.co.kr/" class="footer__family-site">해시코드</a>
</ul>
</section>
<section class="footer__developers">
<h5 class="footer__developers-header">
2021 디벨로퍼스
</h5>
<p class="footer__me-info">이 프로젝트는 HTML, SCSS, TypeScript로 프로그래머스를 직접 클론한 프로젝트입니다. 제 정보는 다음과 같아요! 😘</p>
<a href="" class="">Github 바로가기</a>
<a href="">블로그 바로가기</a>
</section>
</section>
</footer>
이번 건 아무래도 각자가 다 정보가 있기도 했고, 레이아웃 구조도 간단명료해서 시멘틱하게 작성하기 수월했습니다.😂😂
main.scss
그러나 CSS
의 경우... 반응형이 워낙 많은지라 250줄이 되는 코드라 깔끔하게 주요 기능인 드롭다운 기능만 살펴보겠습니다!
&__dropdown {
position: absolute;
top: 1rem;
right: 1rem;
width: 7rem;
// background: white;
&-btn {
display: flex;
align-items: center;
padding: 0.375rem 0.5rem;
border: 1px solid $border-gray-color;
border-radius: 0.25rem;
span {
font-size: 0.6875rem;
font-weight: 700;
letter-spacing: -0.3px;
}
&-arrow {
margin-left: 0.25rem;
border: {
bottom: 3.5px solid black;
left: 3.5px solid transparent;
right: 3.5px solid transparent;
}
transform: rotate(180deg);
}
&--active {
background: $border-gray-color;
}
}
.footer__family-sites {
display: none;
background: white;
position: absolute;
z-index: 30;
&.show-top {
bottom: 1.75rem;
}
&--active {
display: block;
}
width: 100%;
padding: 0.5rem 0;
border: 1px solid $border-gray-color;
border-radius: 5px;
font-size: $font-ms;
box-shadow: 0 0 1rem $border-gray-color;
.footer__family-site {
display: block;
border: none;
padding: 0.125rem 1rem;
color: black;
font-weight: 700;
&:hover {
cursor: pointer;
color: $blue-color;
}
}
}
}
@include customMedia("tablet") {
.footer__dropdown {
right: 0;
position: relative;
.footer__family-sites {
position: absolute;
}
}
}
일단 다 간추렸을 때 이런 식으로 position
값을 조절하며 구현할 수 있었어요.
저는 일단 기준점을 잡았어요.
만약 버튼이 다 안보이는 상태라면 -> 위쪽으로 보이면 됩니다.
만약 버튼이 다 보이는 상태라면 -> 아래쪽으로 보이면 됩니다.
따라서 이를 어떤 기준점 하나를 잡고, 뷰포트에서 다 보이는지를 계속 감시하면 될 것 같다는 생각이 들었고, 따라서 Observer API
중, interserctionObserver
를 사용했답니다!
interserctionObserver
는 다음과 같은 인수를 받아요.
new intersectionObserver(cb, options)
cb
(entries
: 인스턴스의 배열,observer
: 콜백이 실행되는 인스턴스)options
: {
root
: null(viewport) 가시성 검사를 위한 대상,
rootMargin
: 0 루트의 범위를 상하좌우로 늘리거나 줄일 수 있음,
threshold
: 1 얼마나 보여질 때 적용할 것인지
}
interface optionFormat {
root: any,
rootMargin: string,
threshold: number
}
interface Names {
[name: string]: string
}
export default class Footer {
private readonly names: Names;
constructor() {
this.names = {
dropdownBtn: 'footer__dropdown-btn',
familySites: 'footer__family-sites'
}
this.HandleEvent()
}
HandleEvent() {
const $dropdownBtn:HTMLElement = document.querySelector(`.${this.names.dropdownBtn}`);
const $familySites:HTMLElement = document.querySelector(`.${this.names.familySites}`);
let options:optionFormat = {
root: null, // default: null (viewport)
rootMargin: '0px 0px -50px 0px',
threshold: 1,
}
let callback = (entries: any, observer: any) => {
entries.forEach((entry: any) => {
$familySites.classList.toggle('show-top', !entry.isIntersecting)
});
};
let observer = new IntersectionObserver(callback, options);
let target = $dropdownBtn;
observer.observe(target);
$dropdownBtn.addEventListener('click', (e) => {
$dropdownBtn.classList.toggle(`${this.names.dropdownBtn}--active`);
$familySites.classList.toggle(`${this.names.familySites}--active`);
})
}
}
저같은 경우, 일단 드롭다운 버튼을 기준점으로 잡은 후, 뷰포트를 통해 가시성을 관찰했어요.
이때, 드롭다운을 눌렀을 때 어느정도 보이는 크기를 고려하여 rootMargin
을 setting하였습니다.
결과적으로, 드롭다운 내용들이 다 보여질 정도의 크기라면 밑으로 내려가게 하고, 아니라면 위로 올렸죠! (이는 보시면 아시겠지만, 클래스를 이용해서 구현해냈습니다!)
이정도가 딱 밑으로 위치하는 마지노선이네요!
이런 식으로 하나하나 제대로 다져갔어야 했는데, 여태까지 공부방식은 너무나 급하기 그지없었던 것 같아요.
그래도 잘못된 부분을 알았다면! 고치는 게 제 성장을 위한 일이겠죠. 😊
일단 오늘은 이정도로 다시 프로젝트 회고를 마치고, 블로그 프로젝트를 유지보수해보러 가야겠어요.
만약 제 설명이 틀린 부분이 있거나, 더 효율적인 방안이 있다면 말씀해주시면 언제든지 환영합니다! 읽어주셔서 감사합니다 👍😁