서브메뉴 구현¶
서브메뉴는 현재 선택된 메뉴의 하위 메뉴만 표시하는 내비게이션입니다. 주로 사이드바나 페이지 상단에 위치합니다.
기본 서브메뉴¶
선택된 1차 메뉴의 2차 메뉴 표시¶
<!-- 현재 선택된 1차 메뉴 찾기 -->
<nav class="submenu" cond="$main_menu">
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected'] && $val1['list']">
<h3 class="submenu-title">{$val1['link']}</h3>
<ul class="submenu-list">
<li loop="$val1['list'] => $key2, $val2"
class="active"|cond="$val2['selected']">
<a href="{$val2['href']}">{$val2['link']}</a>
</li>
</ul>
</block>
</nav>
3단계까지 표시하는 서브메뉴¶
<aside class="sidebar-menu">
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected']">
<!-- 1차 메뉴 제목 -->
<h2 class="menu-title">
<a href="{$val1['href']}">{$val1['link']}</a>
</h2>
<!-- 2차 메뉴 -->
<ul class="depth2" cond="$val1['list']">
<li loop="$val1['list'] => $key2, $val2"
class="{$val2['selected'] ? 'active' : ''} {$val2['list'] ? 'has-child' : ''}">
<a href="{$val2['href']}">{$val2['link']}</a>
<!-- 3차 메뉴 -->
<ul class="depth3" cond="$val2['list']">
<li loop="$val2['list'] => $key3, $val3"
class="active"|cond="$val3['selected']">
<a href="{$val3['href']}">{$val3['link']}</a>
</li>
</ul>
</li>
</ul>
</block>
</aside>
아코디언 스타일 서브메뉴¶
HTML 구조¶
<nav class="accordion-menu">
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected'] && $val1['list']">
<ul class="accordion-list">
<li loop="$val1['list'] => $key2, $val2"
class="accordion-item {$val2['selected'] ? 'active' : ''}">
<!-- 2차 메뉴 헤더 -->
<div class="accordion-header">
<a href="{$val2['href']}" class="accordion-link">
{$val2['link']}
</a>
<button class="accordion-toggle" cond="$val2['list']">
<i class="icon-chevron-down"></i>
</button>
</div>
<!-- 3차 메뉴 패널 -->
<div class="accordion-panel" cond="$val2['list']">
<ul class="accordion-content">
<li loop="$val2['list'] => $key3, $val3"
class="active"|cond="$val3['selected']">
<a href="{$val3['href']}">{$val3['link']}</a>
</li>
</ul>
</div>
</li>
</ul>
</block>
</nav>
JavaScript¶
jQuery(function($) {
// 아코디언 초기화
$('.accordion-menu').each(function() {
var $menu = $(this);
// 활성 메뉴 열기
$menu.find('.accordion-item.active > .accordion-panel').show();
$menu.find('.accordion-item.active > .accordion-header .icon-chevron-down')
.addClass('rotated');
// 토글 버튼 클릭
$menu.on('click', '.accordion-toggle', function(e) {
e.preventDefault();
var $item = $(this).closest('.accordion-item');
var $panel = $item.find('> .accordion-panel');
var $icon = $(this).find('i');
// 현재 아이템 토글
$panel.slideToggle(300);
$icon.toggleClass('rotated');
// 다른 아이템 닫기 (옵션)
if ($menu.data('close-others')) {
$item.siblings().find('> .accordion-panel').slideUp(300);
$item.siblings().find('.icon-chevron-down').removeClass('rotated');
}
});
});
});
CSS 스타일¶
/* 아코디언 메뉴 */
.accordion-menu {
border: 1px solid #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.accordion-list {
list-style: none;
margin: 0;
padding: 0;
}
.accordion-item {
border-bottom: 1px solid #e0e0e0;
}
.accordion-item:last-child {
border-bottom: none;
}
/* 헤더 */
.accordion-header {
display: flex;
align-items: center;
background: #f8f9fa;
}
.accordion-header:hover {
background: #e9ecef;
}
.accordion-item.active > .accordion-header {
background: #007bff;
}
.accordion-item.active > .accordion-header .accordion-link {
color: white;
}
.accordion-link {
flex: 1;
padding: 15px 20px;
color: #333;
text-decoration: none;
}
.accordion-toggle {
width: 50px;
height: 50px;
background: none;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.accordion-toggle i {
transition: transform 0.3s;
}
.accordion-toggle i.rotated {
transform: rotate(180deg);
}
/* 패널 */
.accordion-panel {
display: none;
background: white;
}
.accordion-content {
list-style: none;
margin: 0;
padding: 0;
}
.accordion-content li {
border-top: 1px solid #f0f0f0;
}
.accordion-content a {
display: block;
padding: 12px 20px 12px 40px;
color: #666;
text-decoration: none;
}
.accordion-content a:hover {
background: #f8f9fa;
color: #333;
}
.accordion-content .active a {
background: #e3f2fd;
color: #007bff;
font-weight: 500;
}
탭 스타일 서브메뉴¶
<nav class="tab-submenu">
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected'] && $val1['list']">
<!-- 탭 헤더 -->
<ul class="tab-header">
<li loop="$val1['list'] => $key2, $val2"
class="tab-item {$val2['selected'] ? 'active' : ''}">
<a href="{$val2['href']}" class="tab-link">
{$val2['link']}
</a>
</li>
</ul>
<!-- 탭 콘텐츠 (3차 메뉴) -->
<div class="tab-content">
<block loop="$val1['list'] => $key2, $val2" cond="$val2['selected'] && $val2['list']">
<ul class="tab-panel active">
<li loop="$val2['list'] => $key3, $val3">
<a href="{$val3['href']}"
class="active"|cond="$val3['selected']">
{$val3['link']}
</a>
</li>
</ul>
</block>
</div>
</block>
</nav>
수직 사이드바 메뉴¶
<aside class="vertical-menu">
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected']">
<!-- 메뉴 헤더 -->
<div class="menu-header">
<h3>{$val1['link']}</h3>
<a href="{$val1['href']}" class="view-all">전체보기 →</a>
</div>
<!-- 메뉴 본문 -->
<div class="menu-body" cond="$val1['list']">
<ul class="menu-list">
<li loop="$val1['list'] => $key2, $val2"
class="menu-item {$val2['selected'] ? 'active' : ''}">
<a href="{$val2['href']}" class="menu-link">
<span class="link-text">{$val2['link']}</span>
<span class="link-badge" cond="$val2['new_count']">
{$val2['new_count']}
</span>
</a>
<!-- 하위 메뉴 -->
<ul class="submenu-list" cond="$val2['list']">
<li loop="$val2['list'] => $key3, $val3">
<a href="{$val3['href']}"
class="active"|cond="$val3['selected']">
- {$val3['link']}
</a>
</li>
</ul>
</li>
</ul>
</div>
</block>
</aside>
플로팅 서브메뉴¶
스크롤 시 따라다니는 서브메뉴:
<div class="floating-submenu" id="floatingMenu">
<nav class="sticky-menu">
<!-- 서브메뉴 내용 -->
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected'] && $val1['list']">
<ul>
<li loop="$val1['list'] => $key2, $val2">
<a href="{$val2['href']}"
class="active"|cond="$val2['selected']"
data-scroll-to="#{$val2['mid']}">
{$val2['link']}
</a>
</li>
</ul>
</block>
</nav>
</div>
<script>
jQuery(function($) {
var $floatingMenu = $('#floatingMenu');
var menuTop = $floatingMenu.offset().top;
var $window = $(window);
// 스크롤 이벤트
$window.scroll(function() {
var scrollTop = $window.scrollTop();
if (scrollTop > menuTop - 20) {
$floatingMenu.addClass('fixed');
} else {
$floatingMenu.removeClass('fixed');
}
});
// 부드러운 스크롤
$floatingMenu.on('click', 'a[data-scroll-to]', function(e) {
e.preventDefault();
var target = $(this).data('scroll-to');
var $target = $(target);
if ($target.length) {
$('html, body').animate({
scrollTop: $target.offset().top - 80
}, 500);
}
});
});
</script>
모바일 서브메뉴¶
슬라이드 메뉴¶
<div class="mobile-submenu">
<!-- 현재 페이지 표시 -->
<button class="current-menu-toggle">
<span class="current-title">
<block loop="$main_menu->list => $key1, $val1">
<block loop="$val1['list'] => $key2, $val2" cond="$val2['selected']">
{$val2['link']}
</block>
</block>
</span>
<i class="icon-chevron-down"></i>
</button>
<!-- 슬라이드 패널 -->
<div class="slide-panel">
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected'] && $val1['list']">
<ul class="mobile-menu-list">
<li loop="$val1['list'] => $key2, $val2">
<a href="{$val2['href']}"
class="active"|cond="$val2['selected']">
{$val2['link']}
</a>
</li>
</ul>
</block>
</div>
</div>
브레드크럼과 연동¶
<div class="page-navigation">
<!-- 브레드크럼 -->
<nav class="breadcrumb">
<a href="{getUrl()}">홈</a>
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected']">
<span class="sep">›</span>
<a href="{$val1['href']}">{$val1['link']}</a>
<block loop="$val1['list'] => $key2, $val2" cond="$val2['selected']">
<span class="sep">›</span>
<a href="{$val2['href']}">{$val2['link']}</a>
<block loop="$val2['list'] => $key3, $val3" cond="$val3['selected']">
<span class="sep">›</span>
<span class="current">{$val3['link']}</span>
</block>
</block>
</block>
</nav>
<!-- 서브메뉴 -->
<nav class="submenu-inline">
<block loop="$main_menu->list => $key1, $val1" cond="$val1['selected'] && $val1['list']">
<ul>
<li loop="$val1['list'] => $key2, $val2">
<a href="{$val2['href']}"
class="active"|cond="$val2['selected']">
{$val2['link']}
</a>
</li>
</ul>
</block>
</nav>
</div>
설정 옵션¶
info.xml에서 서브메뉴 관련 설정:
<extra_vars>
<var name="submenu_type" type="select">
<title xml:lang="ko">서브메뉴 타입</title>
<options value="basic">
<title xml:lang="ko">기본</title>
</options>
<options value="accordion">
<title xml:lang="ko">아코디언</title>
</options>
<options value="tab">
<title xml:lang="ko">탭</title>
</options>
</var>
<var name="submenu_position" type="select">
<title xml:lang="ko">서브메뉴 위치</title>
<options value="sidebar">
<title xml:lang="ko">사이드바</title>
</options>
<options value="top">
<title xml:lang="ko">상단</title>
</options>
</var>
<var name="show_depth3" type="select">
<title xml:lang="ko">3차 메뉴 표시</title>
<options value="Y">
<title xml:lang="ko">표시</title>
</options>
<options value="N">
<title xml:lang="ko">숨김</title>
</options>
</var>
</extra_vars>