ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JS / 바닐라로 앵커트메뉴, 메뉴엑티브, 스티키 만들기
    Web dev/JavaScript 2023. 4. 23. 14:17
    728x90
    반응형

    이전에 스티키 기능만 넣은 메뉴를 만들었다.

    더불어 메뉴를 누르면 앵커드기능과 스크롤위치에 따른 메뉴 엑티브기능을 만들었다.

     

    앵커드 기능

    1) 각 컨텐츠의 위치 구하기 offsetTop; 사용, 제이쿼리는 offset().top 사용

    2) scrollTo({ top:ContentsBoxPosition, behavior:'smooth'}) 메서드 사용, 스무스옵션을 안쓰면 뚝뚝 끊김.

    3) 버튼들에게 이벤트리스너 주기

    anchor(){
                let tabBtn = document.querySelectorAll(".category_list li");
                let contentsBox = document.querySelectorAll(".contents_box");
    
                function addEvent(idx){
                    // let ContentsBoxPosition = contentsBox[idx].getBoundingClientRect().top;
                    // 위처럼 하면 랜딩되었을때 값이 구해져서 사용하기 부적합 따라서 아래처럼 구함
                    
                    let ContentsBoxPosition = contentsBox[idx].offsetTop;
    
                    tabBtn[idx].addEventListener("click",function(){
                    // 제이쿼리를 사용하면 animate() 메서드를 사용할 수 있다.
                        window.scrollTo({ top:ContentsBoxPosition, behavior:'smooth'});
                        js.flag = true;
                    })
                }
                for(i=0; i < tabBtn.length; i++){
                    addEvent(i)
                }
            }

    메뉴 스티키 효과는 이전 글에서 구현해놨었다.

    스크롤 위치에 따른 메뉴 엑티브 효과

    1) 각 컨텐츠 위치구해서 배열에 담기

    2) flag 객체생성, 앵커드기능에서 앵커드이동중일때 동작안하게 하도록 예외처리, 이거 때문에 한참 버벅임..

    const js = {
            flag:false,
            scrollEvt(){
                let categoryInner = document.querySelector(".category_tab_inner");
                let scrollPosition = document.documentElement.scrollTop;
                let tabBtn = document.querySelectorAll(".category_list li");
                let contentsBox = document.querySelectorAll(".contents_box");
                let categoryMenuPosition = document.querySelector(".category_tab_wrap").getBoundingClientRect().top;
                let curContentsArr = []; 
    	
        		// 메뉴 스티키 기능
                if(0 >= categoryMenuPosition){
                    categoryInner.classList.add("fixed");
                }else{
                    categoryInner.classList.remove("fixed");
                }
    			
                // 메뉴 엑티브 효과
                function addActive(i){
                	// 각 컨텐츠 위치구하기 
                    let curCentents = contentsBox[i].offsetTop;
                    curContentsArr.push(curCentents);
    				
                    // 버튼 앵커를 누르면 이동할떄 마다 엑티브 효과가 생겨서 끝날때 한번만 실행하도록했다.
                    // anchor 메뉴 버튼을 누르면 true, 앵커드 후 도착하면 false 
                    if(scrollPosition === curContentsArr[i]){
                        js.flag = false;
                    } 
                    
                    // js.flag가 false 일떄 동작
                    // curContentsArr 각 위치값을 배열에 저장, 체이닝으로 더 줄여써도 되지만 일부러 배열을 한번 더 만듦
                    // 어떤사람은 각 컨텐츠위치값 + 각 컨테츠 높이값을 더해서 조건문을 쓰지만 나중에 아코디언메뉴가 있는 페이지지일때 하면될듯싶어 그냥했다.
                    
                    if(scrollPosition >= curContentsArr[i] && !js.flag){
                        tabBtn.forEach(function(idx){idx.classList.remove("on");});
                        tabBtn[i].classList.add("on");
                    }
                };
               
                for(i=0; i < tabBtn.length; i++){
                    addActive(i)
                }
            },
            anchor(){
                let tabBtn = document.querySelectorAll(".category_list li");
                let contentsBox = document.querySelectorAll(".contents_box");
    
                function addEvent(idx){
                    // let ContentsBoxPosition = contentsBox[idx].getBoundingClientRect().top;
                    let ContentsBoxPosition = contentsBox[idx].offsetTop;
                    // let ContentsBoxHeight = contentsBox[idx].offsetHeight;
    
                    tabBtn[idx].addEventListener("click",function(){
                        window.scrollTo({ top:ContentsBoxPosition, behavior:'smooth'});
                        js.flag = true;
                    })
                }
                for(i=0; i < tabBtn.length; i++){
                    addEvent(i)
                }
            },
            
            init(){
                window.addEventListener("scroll",js.scrollEvt);
                js.anchor();
            }
        }
        js.init();

     

    코드전체

    <!DOCTYPE html>
    <html lang="ko">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>카테고리 탭</title>
    
        <link rel="stylesheet" type="" href="./css/reset.css">
    
        <style>
            .fixed{position: fixed; top: 0; left: 0; right: 0;}
            .visual{height: 500px; background-color: skyblue; margin-bottom: 50px;}
            .contents_box{height: 500px;}
            .footer{height: 500px; background-color: #000;}
    
            .category_tab_wrap{height: 38px; margin: 50px;}
            .category_tab_inner{max-width: 1200px; margin: 0 auto;}
            .category_list{display: flex; justify-content: center;}
            .category_list .item + .item{margin-left: 10px;}
            .category_list .item a{display: block; padding: 10px 15px; border-radius: 50px; border: 1px solid #000; background-color: #fff;}
            .category_list .item.on a{background-color: #000; color: #fff;}
        </style>
    </head>
    <body>
        <div class="visual">비쥬얼</div>
        <div class="category_tab_wrap">
            <div class="category_tab_inner">
                <ul class="category_list">
                    <li class="item on"><a href="javascript:;" title="한식 선택">한식</a></li>
                    <li class="item"><a href="javascript:;" title="양식 선택">양식</a></li>
                    <li class="item"><a href="javascript:;" title="중식 선택">중식</a></li>
                    <li class="item"><a href="javascript:;" title="일식 선택">일식</a></li>
                    <li class="item"><a href="javascript:;" title="기타 선택">기타</a></li>
                </ul>
            </div>
        </div>
        <div class="contents_box" style="background-color: orange;">한식</div>
        <div class="contents_box" style="background-color: yellow;">양식</div>
        <div class="contents_box" style="background-color: red;">중식</div>
        <div class="contents_box" style="background-color: blue;">일식</div>
        <div class="contents_box" style="background-color: skyblue;">기타</div>
        <div class="footer">풋터</div>
    </body>
    <script>
        const js = {
            flag:false,
            scrollEvt(){
                let categoryInner = document.querySelector(".category_tab_inner");
                let scrollPosition = document.documentElement.scrollTop;
                let tabBtn = document.querySelectorAll(".category_list li");
                let contentsBox = document.querySelectorAll(".contents_box");
                // window.pageYOffset
                let categoryMenuPosition = document.querySelector(".category_tab_wrap").getBoundingClientRect().top;
                let curContentsArr = []; 
    
                if(0 >= categoryMenuPosition){
                    categoryInner.classList.add("fixed");
                }else{
                    categoryInner.classList.remove("fixed");
                }
    
                function addActive(i){
                    let curCentents = contentsBox[i].offsetTop;
                    curContentsArr.push(curCentents);
    
                    if(scrollPosition === curContentsArr[i]){
                        js.flag = false;
                    } 
                    if(scrollPosition >= curContentsArr[i] && !js.flag){
                        tabBtn.forEach(function(idx){idx.classList.remove("on");});
                        tabBtn[i].classList.add("on");
                    }
                };
                for(i=0; i < tabBtn.length; i++){
                    addActive(i)
                }
            },
            anchor(){
                let tabBtn = document.querySelectorAll(".category_list li");
                let contentsBox = document.querySelectorAll(".contents_box");
    
                function addEvent(idx){
                    // let ContentsBoxPosition = contentsBox[idx].getBoundingClientRect().top;
                    let ContentsBoxPosition = contentsBox[idx].offsetTop;
                    let ContentsBoxHeight = contentsBox[idx].offsetHeight;
    
                    tabBtn[idx].addEventListener("click",function(){
                        window.scrollTo({ top:ContentsBoxPosition, behavior:'smooth'});
                        js.flag = true;
                    })
                }
                for(i=0; i < tabBtn.length; i++){
                    addEvent(i)
                }
            },
            
            init(){
                window.addEventListener("scroll",js.scrollEvt);
                js.anchor();
            }
        }
        js.init();
    </script>
    </html>

     

    728x90
    반응형

    댓글

Designed by Tistory.