tab tab tab

혜진·2024년 11월 8일
0

웰컴백
오백만년만에 벨로그에 돌아왔습니다
똥멍청이감자 시절을 지나 저도 이제 어엿한
어엿한
1년차 똥멍청이감자가 되었습니다

입사 첫 주에 샀던 일주일 스케줄러를 오늘 다 썼습니다

그간 수많은 일이 있었고 ,, 회사에서 가끔 몰래 눈물도 훔치고 ,,

작년에는 일 하나라도 들어오면
내가 어떻게 해내냐고 ! 당장 10분만에 작업 끝내야 해 왜냐면 다른 분들도 10분 만에 하니까 !
이러면서 오도방정을 떨었는데요

지금은 ,, 작년보다는 덤덤합니다
오류가 나면 바로 얼어버린 채로 옆 자리 짝꿍분께 SOS를 쳤는데, 지금은 작업 마감시간이 급하지 않은 경우라면 끝까지 찾아봅니다. 그래도 진짜 진짜 진짜 안되겠고 머리카락이 다 뽑힐 때쯤 되면 저희 팀 어른들께 도움을 요청합니다.
매일 새로운 걸 하고 새로운 걸 알아가는 직업인지라 긴장은 되지만, 긴장된다고 안 할수는 없으니 긴장하면서 계속 해나가는 1년차입니다.

작년에는 '절대' 라는 말이 습관처럼 붙었다면 올해는 '일단' 이라는 말이 습관처럼 붙었습니다
절대 못할 것 같아요 -> 일단 해볼게요
아주 느리게 자라고 있습니다 가랑비에도 옷은 젖기 마련입니다


10월 말부터 이번 주까지 저는 탭 작업과 그 기능을 설명하는 문서를 만드는 일을 했습니다
문과와 이과의 일을 함께 하니 좌뇌 우뇌가 활성화되는 느낌이라 토하는 줄 알았습니다
진짜 토했냐구요 ?
말이 그렇다는 겁니다

선천적 문과로 태어나 후천적 이과로 살아가고 있지만 너무나 미숙하여 많은 분들의 도움을 받았습니다.
매번 하는 생각이지만 .. 저는 일어날 수 있는 실수의 모든 경우의 수를 모두 경험합니다.
삽질하느라 1주일 정도 쓴 것 같습니다

저희 팀짱님께서는 그런 시간에서 배우는 거고 익숙해지는 거라고 말씀해주십니다

얼마나 속이 터지실지 저로서는 상상할 수 없습니다
그래도 헤맨 만큼 자기 땅이라고 하지 않습니까
모든 실수의 경우의 수를 겪으면 시간은 오래 걸리고 도움도 많이 받아야겠지만
또 한편으로는 그만큼의 데이터가 제게 쌓이는 거라고
그렇게
생각

주십시오 팀짱님
내년에는 똥멍청이감자에서 똥감자까지는 어떻게 자라보겠습니다

네 서론이 길었구요
탭 메뉴를 정리해보겠습니다
다른 프로젝트에서는 background-image 에 on / off 이미지를 넣어서 코드를 짰는데요
이 업계에서는 이것만이 옳다 ! 하는 방법은 없는 것 같습니다
이번 일에서 사용한 방법을 정리해보겠습니다

html 코드

네 일단 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>

javascript 코드

자 숨참고 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";
                        }
                    });
                });
            });
        }
    });

스크롤 위치에 따라 탭 메뉴의 동작을 제어하는 기능을 수행합니다. 주요 기능은 다음과 같습니다

  1. 초기 설정: 페이지가 로드되면 탭 메뉴의 높이를 계산하고 설정합니다.

  2. 탭 메뉴 클릭 시 이동: 탭 항목을 클릭하면 해당 섹션으로 부드럽게 스크롤 이동합니다.

  3. 스크롤에 따른 탭 메뉴 표시/숨김: 특정 위치에서 탭 메뉴가 표시되거나 숨겨지도록 합니다.

  4. 섹션 활성화: 스크롤 위치에 따라 각 섹션에 해당하는 탭 메뉴의 이미지가 활성화되어 표시됩니다.

이렇게 탭 메뉴가 사용자의 스크롤 및 클릭에 반응하여 현재 위치를 반영하도록 동적으로 업데이트됩니다.

css 코드

    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년 반이 지났고 여전히 저의 가장 큰 동기부여는 팀짱님입니다
팀짱님... 짱 !

profile
매일 하는 것보다 중요한 건 그럼에도 불구하고 그만두지 않는 것

0개의 댓글