티스토리 뷰

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>JS UI Components 실습</title>
    <style>
        /* 기본 스타일 */
        body { font-family: sans-serif; line-height: 1.6; padding: 20px; color: #333; }
        section { margin-bottom: 50px; border-bottom: 1px solid #eee; padding-bottom: 30px; }
        h2 { color: #007bff; }

        /* 1. 탭 메뉴 스타일 */
        .tab-btn-group { display: flex; gap: 10px; margin-bottom: 15px; }
        .tab-btn { padding: 10px 20px; cursor: pointer; border: 1px solid #ddd; background: #fff; }
        .tab-btn.active { background: #007bff; color: #fff; border-color: #007bff; }
        .tab-content { display: none; padding: 20px; border: 1px solid #ddd; }
        .tab-content.active { display: block; }

        /* 2. 아코디언 스타일 */
        .acc-item { border: 1px solid #ddd; margin-bottom: 5px; border-radius: 4px; overflow: hidden; }
        .acc-btn { width: 100%; padding: 15px; text-align: left; background: #f8f9fa; border: none; cursor: pointer; font-weight: bold; }
        .acc-btn:hover { background: #e2e6ea; }
        .acc-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-out; background: #fff; }
        .acc-inner { padding: 15px; border-top: 1px solid #eee; }

        /* 3. 모달 스타일 */
        .gallery { display: flex; gap: 10px; }
        .gallery img { width: 150px; cursor: pointer; border-radius: 8px; transition: transform 0.2s; }
        .gallery img:hover { transform: scale(1.05); }

        .modal-overlay { 
            position: fixed; top: 0; left: 0; width: 100%; height: 100%; 
            background: rgba(0,0,0,0.8); display: none; align-items: center; justify-content: center; 
        }
        .modal-overlay.active { display: flex; }
        .modal-content { position: relative; max-width: 80%; }
        .modal-content img { width: 100%; border-radius: 10px; }
        .close-btn { 
            position: absolute; top: -40px; right: 0; color: #fff; 
            font-size: 30px; cursor: pointer; 
        }
    </style>
</head>
<body>

    <section>
        <h2>1. 탭 메뉴</h2>
        <div class="tab-btn-group">
            <button class="tab-btn active">메뉴 A</button>
            <button class="tab-btn">메뉴 B</button>
        </div>
        <div class="tab-content-group">
            <div class="tab-content active">첫 번째 탭의 내용입니다.</div>
            <div class="tab-content">두 번째 탭의 내용입니다.</div>
        </div>
    </section>

    <section>
        <h2>2. 아코디언</h2>
        <div class="acc-item">
            <button class="acc-btn">Q1. 자바스크립트가 어렵나요?</button>
            <div class="acc-content">
                <div class="acc-inner">네, 하지만 직접 짜보면 재미있습니다!</div>
            </div>
        </div>
        <div class="acc-item">
            <button class="acc-btn">Q2. 퍼블리셔도 로직을 알아야 하나요?</button>
            <div class="acc-content">
                <div class="acc-inner">그럼요! 유지보수와 소통에 큰 도움이 됩니다.</div>
            </div>
        </div>
    </section>

    <section>
        <h2>3. 모달 팝업 (갤러리)</h2>
        <div class="gallery">
            <img src="https://picsum.photos/id/10/400/300" alt="이미지1">
            <img src="https://picsum.photos/id/20/400/300" alt="이미지2">
        </div>

        <div class="modal-overlay" id="modalOverlay">
            <div class="modal-content">
                <span class="close-btn">&times;</span>
                <img src="" alt="확대 이미지" id="modalImg">
            </div>
        </div>
    </section>

    <script>
        /**
         * 1. 탭 메뉴 로직
         */
        const tabBtns = document.querySelectorAll('.tab-btn');
        const tabContents = document.querySelectorAll('.tab-content');

        tabBtns.forEach((btn, index) => {
            btn.addEventListener('click', () => {
                // 초기화
                tabBtns.forEach(b => b.classList.remove('active'));
                tabContents.forEach(c => c.classList.remove('active'));
                // 활성화
                btn.classList.add('active');
                tabContents[index].classList.add('active');
            });
        });

        /**
         * 2. 아코디언 로직
         */
        const accBtns = document.querySelectorAll('.acc-btn');

        accBtns.forEach(btn => {
            btn.addEventListener('click', function() {
                const content = this.nextElementSibling;
                
                // toggle 처럼 동작하게 함
                if (content.style.maxHeight && content.style.maxHeight !== '0px') {
                    content.style.maxHeight = '0px';
                } else {
                    // 열기 전 모든 아코디언 닫기 (선택사항)
                    // document.querySelectorAll('.acc-content').forEach(c => c.style.maxHeight = '0px');
                    content.style.maxHeight = content.scrollHeight + 'px';
                }
            });
        });

        /**
         * 3. 모달 로직
         */
        const galleryImgs = document.querySelectorAll('.gallery img');
        const modal = document.getElementById('modalOverlay');
        const modalImg = document.getElementById('modalImg');
        const closeBtn = document.querySelector('.close-btn');

        // 이미지 클릭 시 열기
        galleryImgs.forEach(img => {
            img.addEventListener('click', () => {
                const src = img.getAttribute('src');
                modalImg.setAttribute('src', src);
                modal.classList.add('active');
            });
        });

        // 닫기 버튼 클릭 시
        closeBtn.addEventListener('click', () => modal.classList.remove('active'));

        // 배경 클릭 시 닫기
        modal.addEventListener('click', () => modal.classList.remove('active'));

        // 버블링 방지 (이미지 클릭 시엔 안 닫히게)
        modalImg.addEventListener('click', (e) => e.stopPropagation());
    </script>
</body>
</html>

 

※ 해당 내용은 Google Gmini3.0에서 작성되었습니다.

 

탭_아코디언_모달.html
0.01MB

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함