原生JavaScript实现带收放动画效果的导航菜单

键盘旁的猫
键盘旁的猫
发布时间:2026-10-13 | 6人浏览过
原创

掌握如何使用原生JavaScript为网站添加动态交互效果,本文详细解析两种导航菜单特效:第一种利用JS变量控制透明度与容器高度,实现丝滑的收放动画;第二种结合CSS定位与事件委托,构建简洁的下拉菜单结构,助你快速提升前端页面表现力。

实现目标

导航菜单是网页中常见的交互组件。为了提升用户体验,我们通常希望菜单在展开和收起时具有平滑的过渡动画(如高度变化、透明度渐变),而不是生硬的显示与隐藏。本文将分享两个用原生 JavaScript 编写的案例。

准备条件

  • 了解基础的 HTML 结构与 CSS 属性(如 position, opacity, display)。
  • 掌握 JavaScript 的事件监听(addEventListener)、DOM 元素获取及基础动画循环逻辑。
  • 无需引入任何第三方库(如 jQuery),纯原生实现以保持轻量。

第1步:实现带有伸缩动画效果的嵌套菜单

目的:实现当用户点击主菜单项上的特定按钮时,子菜单能够根据当前状态平滑展开或收起,同时伴随高度的变化和透明度的渐变。

入口:创建一个标准的 HTML 页面,定义一个无序列表(<ul>)作为主菜单。

动作与参数:

  1. 结构搭建:将菜单项放入 <li> 标签内。每个可展开的子菜单包含一个触发按钮(<span class="show-hide">)和一个内容容器(<div class="a-div">)。
  2. 样式定义:使用 CSS 设置容器的默认隐藏状态(display: none 或 opacity: 0)。关键在于给容器加上 transition 属性或使用 JS 手动更新样式来实现动画。
  3. 事件绑定:为父级列表绑定 click 事件监听器。当检测到点击的是 .show-hide 元素时,执行动画函数。
  4. 动画逻辑:
  • 判断当前容器是否可见(检查 opacity 是否大于 0.5)。
  • 如果是要“打开”,则初始化起始值为 0,结束值为预设高度(如 90px)。通过 setTimeout 递归调用,每隔约 16.7ms 增加一点高度和透明度,直到达到目标值。
  • 如果是要“关闭”,则反之,逐渐减少高度和透明度至 0,完成后将 display 设为 none。

以下是完整的核心实现代码。注意处理了不同浏览器的兼容性写法(虽然现代浏览器大多支持标准 API,但保留了 attachEvent 以备旧版 IE 需求):

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title></title>
 <script>
 window.onload = function() {
     var ul = document.getElementById('ul');
     if(ul.addEventListener){
         ul.addEventListener('mouseover',listener1,true);
         ul.addEventListener('mouseout',listener2,true);
         ul.addEventListener('click',listener3,false);
     } else if (ul.attachEvent) { //兼容IE8及以前版本
         ul.attachEvent('onmouseover', listener1, false);
         ul.attachEvent('onmouseout', listener2, false);
         ul.attachEvent('onclick', listener3, false);
     }
 
     // mouseover 悬停显示子菜单框
     function listener1(event){
         var target = event.target || event.srcElement;
         if(target.tagName.toLowerCase() === 'li') {
             var div1 = target.getElementsByTagName('div')[0];
             div1.style.display = 'block';
             div1.style.opacity = 1;
         }
     }
     
     // mouseout 移除鼠标后隐藏子菜单框
     function listener2(event){
         var target = event.target || event.srcElement;
         if(target.tagName.toLowerCase() === 'li'){
             var div1 = target.getElementsByTagName('div')[0];
             div1.style.display = 'none';
             div1.style.opacity = 0;
         }
     }
 
     var bool = true;
     // click 点击特定按钮触发动画展开/收起
     function listener3(event) {
         var event = event || window.event;
         var target = event.target || event.srcElement;
         if (target.className === 'show-hide') {
             var adiv = target.nextElementSibling;
             // 判断当前状态来决定是展开还是收起
             if (window.getComputedStyle(adiv, null).opacity > 0.5){bool=false}else{bool=true}
             var height = 90,
                 changeH,
                 opacity,
                 id;
             if (bool) {
                 // 展开动画
                 changeH = 0;
                 opacity = 0;
                 var text = target.innerHTML.slice(0,-1);
                 target.innerHTML = text+' -';
                 (function show() {
                     if (changeH > height) {clearTimeout(id);return}
                     changeH += 5;
                     opacity += 0.06;
                     adiv.style.height = changeH + 'px';
                     adiv.style.opacity = opacity;
                     adiv.style.display = 'block';
                     id = setTimeout(function () {
                         clearTimeout(id);
                         show();
                     }, 16.7);
                 })();
                 bool = false;
             } else {
                 // 收起动画
                 changeH = height;
                 opacity = 1;
                 var text = target.innerHTML.slice(0,-1);
                 target.innerHTML = text+' +';
                 (function hidden() {
                     if (changeH < 0) {clearTimeout(id);adiv.style.display = 'none';return}
                     changeH -= 10;
                     opacity -= 0.11;
                     adiv.style.height = changeH + 'px';
                     adiv.style.opacity = opacity;
                     id = setTimeout(function () {
                         clearTimeout(id);
                         hidden();
                     }, 16.7);
                 })();
                 bool = true;
             }
         }
     }
 };
 </script>
</head>
<body>
<!-- HTML 结构示例 -->
<ul id="ul">
    <li>JavaScript实战
        <div class="nav-div">
            <span class="show-hide">导航特效 +</span>
            <div class="a-div">
                <a class="a" href="">可收放子菜单</a>
                <a class="a" href="">切换页面</a>
            </div>
        </div>
    </li>
</ul>
</body>
</html>

实现该特效后的视觉效果如下:点击带有加号/减号的标题栏,下方的链接区域会根据设定的速率平滑地变大变小。

第2步:实现基于CSS布局的下拉菜单

目的:实现一个更简单、性能更好的纯视觉反馈下拉菜单。主要依靠 CSS 的属性控制,配合少量的 JS 进行事件分发。

入口:同样使用无序列表结构,但层级关系变为扁平化。

动作与参数:

  1. CSS布局:使用绝对定位(position: absolute)将子内容块(.contain)定位在对应的列表项正下方。通过 display: none 将其隐藏在初始状态下。
  2. JS事件:监听父容器的 mouseover 和 mouseout 事件。当鼠标移入 <li> 或其内部元素时,找到对应的兄弟节点或子节点并设置 style.display = 'block';移出时设为 none。
  3. 简化逻辑:这种方案不需要复杂的帧动画计算,加载速度更快,适合移动端或对性能要求极高的场景。
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <style>
 *{ margin: 0; padding: 0; }
 #menu{ width: 400px; border: 1px solid gray; background-color: lemonchiffon; }
 #menu li{ float: left; width: 99px; list-style: none; }
 .contain{ position: absolute; display: none; width: 399px; background-color: lemonchiffon; color: #336699; border: 1px solid gray; top: 30px; left: 0; }
 </style>
</head>
<script>
 window.onload = function(){
   var menu = document.getElementById('menu');
   if(menu.addEventListener){
      menu.addEventListener('mouseover',show,false);
      menu.addEventListener('mouseout',hide,false);
   } else if(menu.attachEvent){ //兼容旧版IE
      menu.attachEvent('onmouseover',show,false);
      menu.attachEvent('onmouseout',hide,false);
   }
   function show(event){
      var target = event.target || event.srcElement;
      // 找到关联的内容层并显示
      if(target.tagName.toLowerCase() === 'li'){
        target.firstElementChild.style.display = 'block';
      } else if(target.parentNode.tagName.toLowerCase() === 'li'){
        target.style.display = 'block';
      }
   }
   function hide(event){
      var target = event.target || event.srcElement;
      // 找到关联的内容层并隐藏
      if(target.tagName.toLowerCase() === 'li'){
        target.firstElementChild.style.display = 'none';
      } else if(target.parentNode.tagName.toLowerCase() === 'li'){
        target.style.display = 'none';
      }
   }
 }
</script>
<body>
<div id="menu">
    <ul>
        <li id="menu1">苏福的特效1
            <div class="contain">内容区域 111...</div>
        </li>
        <li id="menu2">苏福的特效2
            <div class="contain">内容区域 222...</div>
        </li>
        <li id="menu3">苏福的特效3
            <div class="contain">内容区域 333...</div>
        </li>
        <li id="menu4">苏福的特效4
            <div class="contain">内容区域 444...</div>
        </li>
    </ul>
</div>
</body>
</html>

该方案的运行效果如下所示:鼠标悬停在顶部栏目上,下方即弹出对应面板。

以上就是原生JavaScript制作带收放动画效果导航菜单的详细内容,更多关于JavaScript前端技巧的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表