- 纯CSS方法:实现起来最简单,无需JS,但只能实现纯下拉,交互性稍弱。
- CSS + JavaScript方法:可以实现“鼠标悬停显示/点击显示”等更丰富的交互效果,是目前最主流和推荐的方法。
准备工作:获取顶级栏目
无论使用哪种方法,我们首先需要获取顶级栏目,然后在这些顶级栏目下查找它们的子栏目。

(图片来源网络,侵删)
在织梦中,顶级栏目的 topid 属性为 0,我们可以通过 typeid 参数来指定父栏目,从而获取其子栏目。
纯CSS实现下拉菜单
这种方法的核心是利用CSS的 hover 伪类来控制子菜单的显示和隐藏。
第1步:修改模板文件
在你需要显示导航栏的模板文件中(通常是 head.htm 或 templets/default/head.htm),找到导航栏的HTML结构,并将其修改为以下格式:
<div id="nav">
<ul>
{dede:channel type='top' row='8'}
<li>
<a href="[field:typeurl/]">[field:typename/]</a>
<!-- 开始判断是否有子栏目 -->
[field:sonlist runphp='yes']
global $dsql;
$typeid = @me;
$sql = "SELECT id,typename,typedir FROM `dede_arctype` WHERE reid='$typeid' ORDER BY sortrank";
$sonids = '';
$dsql->SetQuery($sql);
$dsql->Execute();
while($row = $dsql->GetArray()){
$sonids .= "<li><a href='/".str_replace('{cmspath}','',$row['typedir'])."/'>".$row['typename']."</a></li>\r\n";
}
if($sonids != ''){
@me = '<ul class="sub-nav">'.$sonids.'</ul>';
}else{
@me = '';
}
[/field:sonlist]
</li>
{/dede:channel}
</ul>
</div>
代码解释:

(图片来源网络,侵删)
{dede:channel type='top' row='8'}: 获取顶级栏目,最多显示8个。[field:typeurl/]: 栏目链接。[field:typename/]: 栏目名称。[field:sonlist runphp='yes']: 这是核心部分,它为每一个顶级栏目都执行一次PHP代码。global $dsql;: 调用数据库操作类。$typeid = @me;: 获取当前顶级栏目的ID。SELECT ... WHERE reid='$typeid': 查询所有reid(父栏目ID)等于当前顶级栏目ID的子栏目。if($sonids != ''): 判断查询结果是否为空,如果不为空,则生成一个<ul class="sub-nav">...</ul>的结构;如果为空,则什么都不输出。
第2步:编写CSS样式
在模板的CSS文件(通常是 style.css 或 templets/default/style/dedecms.css)中,添加以下样式:
/* 基础导航样式 */
#nav {
width: 100%;
background-color: #333; /* 导航背景色 */
font-family: Arial, sans-serif;
}
#nav ul {
list-style: none; /* 去掉列表前的点 */
margin: 0;
padding: 0;
display: flex; /* 使用Flex布局让菜单项水平排列 */
justify-content: center; /* 居中对齐 */
}
#nav li {
position: relative; /* 关键:为下拉菜单定位提供参考 */
margin: 0 10px;
}
#nav li a {
display: block;
color: #fff;
padding: 15px 20px;
text-decoration: none;
text-align: center;
}
#nav li a:hover {
background-color: #555; /* 鼠标悬停时的背景色 */
}
/* --- 二级下拉菜单样式 --- */
#nav li > ul.sub-nav {
/* 默认隐藏 */
display: none;
position: absolute; /* 绝对定位,相对于父级li */
top: 100%; /* 定位在父级li的底部 */
left: 0;
background-color: #444;
min-width: 160px;
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
z-index: 1000;
}
/* 鼠标悬停在父级li上时,显示二级菜单 */
#nav li:hover > ul.sub-nav {
display: block;
}
/* 二级菜单的列表项样式 */
#nav .sub-nav li {
width: 100%;
margin: 0;
}
#nav .sub-nav li a {
text-align: left;
padding: 10px 15px;
}
#nav .sub-nav li a:hover {
background-color: #555;
}
/* --- 三级下拉菜单样式 --- */
#nav .sub-nav li > ul {
display: none;
position: absolute;
top: 0;
left: 100%; /* 定位在二级菜单的右侧 */
background-color: #555;
}
#nav .sub-nav li:hover > ul {
display: block;
}
CSS解释:
#nav li { position: relative; }: 这是实现下拉菜单的关键,它让子菜单可以相对于li元素进行定位。#nav li > ul.sub-nav { display: none; }: 默认隐藏所有二级菜单。#nav li:hover > ul.sub-nav { display: block; }: 当鼠标移动到li上时,它的直接子元素ul.sub-nav(二级菜单)会显示出来。- 三级菜单的原理和二级菜单完全一样,只是定位的父级变成了二级菜单的
li,并且通过left: 100%让它出现在二级菜单的右侧。
CSS + JavaScript实现(推荐)
纯CSS方法在移动端上体验不佳(鼠标悬停不触发),且无法实现点击收起/展开,结合JS可以解决这些问题。
第1步:修改模板文件(HTML结构)
JS方法通常需要一个固定的结构来方便操作,我们可以稍微简化一下HTML,通过JS来动态判断并生成子菜单。

(图片来源网络,侵删)
<div id="nav">
<ul>
{dede:channel type='top' row='8'}
<li>
<a href="[field:typeurl/]">[field:typename/]</a>
<!-- 子菜单容器,JS会动态填充内容 -->
<ul class="sub-nav"></ul>
</li>
{/dede:channel}
</ul>
</div>
第2步:编写CSS样式
CSS部分基本和第一种方法一样,但我们可以移除 hover 的部分,改用 .active 类来控制显示。
/* 基础样式和之前一样,这里省略... */
/* 子菜单默认隐藏 */
#nav li > ul.sub-nav {
display: none;
position: absolute;
top: 100%;
left: 0;
background-color: #444;
min-width: 160px;
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
z-index: 1000;
}
/* 当父级li拥有 .active 类时,子菜单显示 */
#nav li.active > ul.sub-nav {
display: block;
}
/* 三级菜单样式也和之前一样,通过 .active 类控制 */
#nav .sub-nav li > ul {
display: none;
position: absolute;
top: 0;
left: 100%;
background-color: #555;
}
#nav .sub-nav li.active > ul {
display: block;
}
第3步:编写JavaScript代码
在模板文件的底部(</body> 标签之前)引入或编写JS代码。
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取所有顶级导航项
const navItems = document.querySelectorAll('#nav > ul > li');
navItems.forEach(item => {
const link = item.querySelector('a');
const subNav = item.querySelector('.sub-nav');
// 如果存在子菜单,则为其添加事件
if (subNav && subNav.children.length > 0) {
// 鼠标悬停事件
item.addEventListener('mouseenter', () => {
item.classList.add('active');
});
item.addEventListener('mouseleave', () => {
item.classList.remove('active');
});
// 点击事件(可选,用于移动端)
link.addEventListener('click', function(e) {
// 如果是在移动端,可以阻止默认跳转,显示子菜单
// if (window.innerWidth <= 768) {
// e.preventDefault();
// item.classList.toggle('active');
// }
});
}
});
});
</script>
JS解释:
document.addEventListener('DOMContentLoaded', ...):确保DOM元素完全加载后再执行脚本。document.querySelectorAll('#nav > ul > li'):选择所有顶级菜单项。forEach:遍历每一个菜单项。item.addEventListener('mouseenter', ...):当鼠标进入一个菜单项时,给它添加.active类。item.addEventListener('mouseleave', ...):当鼠标离开时,移除.active类。- CSS中定义的
.active类会触发display: block,从而显示子菜单。
如何选择?
- 如果你的网站是纯PC端,且追求最简单的实现:选择方法一(纯CSS)。
- 如果你的网站需要兼容移动端,或者希望有更灵活的交互(比如点击展开):强烈推荐方法二(CSS + JavaScript),这是目前业界最标准和通用的做法。
重要提示:在实际项目中,请务必根据你自己的网站主题样式来调整CSS中的颜色、字体、间距等属性,以达到最佳视觉效果。
