웰컴백
오백만년만에 벨로그에 돌아왔습니다
똥멍청이감자 시절을 지나 저도 이제 어엿한
어엿한
1년차 똥멍청이감자가 되었습니다
입사 첫 주에 샀던 일주일 스케줄러를 오늘 다 썼습니다
그간 수많은 일이 있었고 ,, 회사에서 가끔 몰래 눈물도 훔치고 ,,
작년에는 일 하나라도 들어오면
내가 어떻게 해내냐고 ! 당장 10분만에 작업 끝내야 해 왜냐면 다른 분들도 10분 만에 하니까 !
이러면서 오도방정을 떨었는데요
지금은 ,, 작년보다는 덤덤합니다
오류가 나면 바로 얼어버린 채로 옆 자리 짝꿍분께 SOS를 쳤는데, 지금은 작업 마감시간이 급하지 않은 경우라면 끝까지 찾아봅니다. 그래도 진짜 진짜 진짜 안되겠고 머리카락이 다 뽑힐 때쯤 되면 저희 팀 어른들께 도움을 요청합니다.
매일 새로운 걸 하고 새로운 걸 알아가는 직업인지라 긴장은 되지만, 긴장된다고 안 할수는 없으니 긴장하면서 계속 해나가는 1년차입니다.
작년에는 '절대' 라는 말이 습관처럼 붙었다면 올해는 '일단' 이라는 말이 습관처럼 붙었습니다
절대 못할 것 같아요 -> 일단 해볼게요
아주 느리게 자라고 있습니다 가랑비에도 옷은 젖기 마련입니다
10월 말부터 이번 주까지 저는 탭 작업과 그 기능을 설명하는 문서를 만드는 일을 했습니다
문과와 이과의 일을 함께 하니 좌뇌 우뇌가 활성화되는 느낌이라 토하는 줄 알았습니다
진짜 토했냐구요 ?
말이 그렇다는 겁니다
선천적 문과로 태어나 후천적 이과로 살아가고 있지만 너무나 미숙하여 많은 분들의 도움을 받았습니다.
매번 하는 생각이지만 .. 저는 일어날 수 있는 실수의 모든 경우의 수를 모두 경험합니다.
삽질하느라 1주일 정도 쓴 것 같습니다
저희 팀짱님께서는 그런 시간에서 배우는 거고 익숙해지는 거라고 말씀해주십니다
만
얼마나 속이 터지실지 저로서는 상상할 수 없습니다
그래도 헤맨 만큼 자기 땅이라고 하지 않습니까
모든 실수의 경우의 수를 겪으면 시간은 오래 걸리고 도움도 많이 받아야겠지만
또 한편으로는 그만큼의 데이터가 제게 쌓이는 거라고
그렇게
생각
해
주십시오 팀짱님
내년에는 똥멍청이감자에서 똥감자까지는 어떻게 자라보겠습니다
네 서론이 길었구요
탭 메뉴를 정리해보겠습니다
다른 프로젝트에서는 background-image 에 on / off 이미지를 넣어서 코드를 짰는데요
이 업계에서는 이것만이 옳다 ! 하는 방법은 없는 것 같습니다
이번 일에서 사용한 방법을 정리해보겠습니다
네 일단 html 코드는 이렇게 정리가 됩니다
div 태그 안에 id로 묶어 a 태그 안에 off / on 순서로 이미지를 배열합니다
탭 메뉴 하나마다 class 과 data-target 을 정해 스크롤 이벤트를 설정합니다
div 로 섹션을 나눕니다.
id 에는 data-target 를 각각 넣어주고, foreach 문이 돌아야하기에 class 로 section--을 공통적으로 넣어줍니다
마지막으로는 이제 탭에 해당되는 섹션이 끝났을 때, 탭메뉴가 보이지 않아야 할만큼의 스크롤을 내리면 탭 메뉴가 사라지는 기능을 넣기 위해
탭 메뉴 섹션이 끝난 바로 다음 이미지에 id로 end 를 넣어줍니다
콘텐츠가 길어지고 탭 메뉴가 많아져도 이 형식을 유지하면 됩니다
<div style="width:100%; max-width: 800px; margin:0 auto;">
//탭 메뉴//
<div>
<div id="tab-menu-fix--" style="display: flex;" class="">
<div class="tab--" data-target="#section1">
<a href="#none">
<img src="tab1_off.jpg" class="default-img--" style="width: 100%;">
<img src="tab1_on.jpg" class="active-img--" style="width: 100%;">
</a>
</div>
<div class="tab--" data-target="#section2">
<a href="#none">
<img src="tab2_off.jpg" class="default-img--" style="width: 100%;">
<img src="tab2_on.jpg" class="active-img--" style="width: 100%;">
</a>
</div>
<div class="tab--" data-target="#section3">
<a href="#none">
<img src="tab3_off.jpg" class="default-img--" style="width: 100%;">
<img src="tab3_on.jpg" class="active-img--" style="width: 100%;">
</a>
</div>
</div>
</div>
//탭 메뉴//
//section //
<div style="display:flex;">
<div>
<div id="event01">
<div style="display:flex;">
<div>
<div class="section--" id="section1">
<div>
<img src="section1Image.jpg" alt="" style="width: 100%;" class="">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="display:flex;">
<div>
<div id="event02">
<div style="display:flex;">
<div>
<div class="section--" id="section2">
<div>
<img src="section2Image.jpg" alt="" style="width: 100%;" class="">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style="display:flex;">
<div>
<div id="event03">
<div style="display:flex;">
<div>
<div class="section--" id="section3">
<div>
<img src="section3Image.jpg" alt="" style="width: 100%;" class="">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
//section //
//탭 메뉴가 사라질 section//
<div id="tab-end">
<img src="endImage.jpg" alt="" style="width: 100%;" class="">
</div>
//탭 메뉴가 사라질 section//
</div>
</div>
</div>
자 숨참고 js 다이브 가겠습니다
숨 크게 허어어어어업 !
렛츠 고
function onImageLoad(imageSelector, callback) {
const imageElement = document.querySelector(imageSelector);
// 이미지 요소가 없을 경우 종료
if (!imageElement) {
console.warn("이미지를 찾을 수 없습니다:", imageSelector);
return;
}
// 이미지가 이미 로딩이 완료된 상태일 경우
if (imageElement.complete) {
callback(); // 이미지가 이미 로드된 경우 즉시 콜백 실행
} else {
// 이미지 로딩이 완료되었을 때 이벤트 핸들러 등록
imageElement.onload = callback(); // 콜백을 함수 참조로 전달
imageElement.onerror = () => {
console.error("이미지 로딩 실패:", imageSelector);
};
}
}
이미지 로드 상태를 확인하고 로드 완료 시 콜백을 호출합니다.
function getOffsetTop(element) {
let offsetTop = 0;
// 부모 요소들을 따라가면서 모든 offsetTop 값을 합산
while (element) {
offsetTop += element.offsetTop;
element = element.offsetParent;
}
return offsetTop;
}
이 함수는 특정 요소의 상단 위치를, 해당 요소와 모든 부모 요소의 상단 위치(offsetTop)를 합산하여 계산합니다.
function fixHeaderHeightReturn() {
return 95;
}
페이지의 다른 요소들을 배치하거나 특정 위치를 계산할 때, 고정된 헤더 높이를 fixHeaderHeightReturn()을 통해 쉽게 가져와 사용할 수 있습니다.
window.addEventListener("load", function() {
const _eventTab = document.getElementById("tab-menu-fix--");
let fixHeaderHeight = fixHeaderHeightReturn();
if (_eventTab) {
onImageLoad("#tab-menu-fix--", () => {
let tabHeight = Math.ceil(_eventTab.offsetHeight);
fixHeaderHeight = fixHeaderHeightReturn();
let fixedHeight = tabHeight + fixHeaderHeight;
_eventTab.parentElement.style.height = `${tabHeight}px`;
_eventTab.style.height = `${tabHeight}px`;
const tabLinks = _eventTab.querySelectorAll("div a");
tabLinks.forEach(link => {
link.addEventListener("click", (event) => {
event.preventDefault();
fixHeaderHeight = fixHeaderHeightReturn();
fixedHeight = tabHeight + fixHeaderHeight;
const targetId = link.parentElement.getAttribute('data-target');
const targetElement = document.querySelector(targetId);
if (targetElement) {
const offsetTop = getOffsetTop(targetElement) - (fixedHeight - 1);
window.scrollTo({top: offsetTop, behavior: "smooth"});
}
});
});
window.addEventListener("scroll", () => {
let windowScrollTop = window.scrollY;
const tabEnd = document.getElementById("end");
let hidePosition = tabEnd ? tabEnd.offsetTop : 0;
fixHeaderHeight = fixHeaderHeightReturn();
fixedHeight = tabHeight + fixHeaderHeight;
if (windowScrollTop >= hidePosition && hidePosition !== 0) {
_eventTab.style.display = "none";
} else {
_eventTab.style.display = "flex";
}
const section1Top = getOffsetTop(document.getElementById("section1")) - fixedHeight;
if (windowScrollTop >= section1Top) {
_eventTab.classList.add("on");
} else {
_eventTab.classList.remove("on");
}
const sections = document.querySelectorAll(".section--");
sections.forEach(section => {
let sectionTop = getOffsetTop(section) - fixedHeight;
let sectionHeight = section.offsetHeight;
if (windowScrollTop >= (sectionTop - 1) && windowScrollTop < sectionTop + sectionHeight) {
const targetId = section.getAttribute("id");
const targetTab = _eventTab.querySelector(`.tab--[data-target="#${targetId}"]`);
const activeImages = _eventTab.querySelectorAll(".active-img--");
activeImages.forEach(img => (img.style.display = "none"));
const activeImage = targetTab.querySelector(".active-img--");
if (activeImage) activeImage.style.display = "block";
}
});
});
});
}
});
스크롤 위치에 따라 탭 메뉴의 동작을 제어하는 기능을 수행합니다. 주요 기능은 다음과 같습니다
초기 설정: 페이지가 로드되면 탭 메뉴의 높이를 계산하고 설정합니다.
탭 메뉴 클릭 시 이동: 탭 항목을 클릭하면 해당 섹션으로 부드럽게 스크롤 이동합니다.
스크롤에 따른 탭 메뉴 표시/숨김: 특정 위치에서 탭 메뉴가 표시되거나 숨겨지도록 합니다.
섹션 활성화: 스크롤 위치에 따라 각 섹션에 해당하는 탭 메뉴의 이미지가 활성화되어 표시됩니다.
이렇게 탭 메뉴가 사용자의 스크롤 및 클릭에 반응하여 현재 위치를 반영하도록 동적으로 업데이트됩니다.
img {
vertical-align: top;
}
.tab-- {
position: relative;
width: 100%;
overflow: hidden;
}
.tab-- img {
width: 100%;
object-fit: cover;
height: 100% !important;
}
.tab-- a {
display: flex;
width: 100%;
height: 100%;
}
.w_50 {
width: 50%;
}
.w_25 {
width: 25%;
}
.tab-- a .active-img-- {
position: absolute;
top: 0;
display: none
}
#tab-menu-fix-- {
width: 100%;
display: flex;
flex-wrap: wrap;
position: relative;
z-index: 9;
}
#tab-menu-fix--.on {
position: fixed;
top: 94px;
width: 860px;
z-index: 9;
}
#tab-menu-fix-- > a .tab-- {
cursor: pointer;
position: relative;
}
이렇게 정리하면 멋드러진 탭 메뉴가 기능하는 페이지가 완성됩니다
html 구조도 js 코드도 더 숙련이 필요하겠습니다
다음에는 좀 더 잘해볼 수 있을 것 같습니다
타팀 교육을 하게 될 줄은 몰랐습니다
저의 선생님 경력(대학생 때 수능영어알바 / 어린이집 교사 20개월)이 도움이..
됐을까요 ?
사실 팀짱님이 다 해주셨고 저는 깍두기였습니다
너무 긴장돼서 교육 끝나고 화장실 가서 토했어요
진짜 했냐구요 ?
네 이번엔 진짜 했습니다
넘 떨렸어요
좀 더 실수없이 잘했었다면 좋았을 것 같습니다
그래도 다음에는 기필코 오늘보다 반드시 더 발전하고 능숙해져있을테니
너무 많은 걱정은 미리 하지 않겠습니다
1년 반이 지났고 여전히 저의 가장 큰 동기부여는 팀짱님입니다
팀짱님... 짱 !