You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

纯CSS实现下拉菜单展开时保持按钮下划线动画常驻

问题描述

纯CSS实现简易下拉菜单,全程不使用JavaScript,目前存在交互问题:

  • 下拉菜单打开时,要求菜单按钮的下划线动画保持width: 100%的激活状态
  • 实际表现:鼠标移动到展开的下拉菜单位置时,下划线动画会回到初始width: 0状态
  • 约束要求:下划线动画元素必须保留在<a>标签(即TESTIMONIALS导航选项卡)上,不能把.line-anim类移动到<li>元素和.dropdown-menu-hook同级(虽然该写法能实现预期效果,但不符合结构要求)
  • 已尝试无效方案:直接给.line-anim:after设置width: 100%!important;,该写法会丢失平滑过渡动画效果
  • 目标效果:下划线平滑过渡到100%宽度后保持激活状态,直到鼠标完全移出下拉菜单区域才收回。
现有实现代码

问题版本(.line-anim挂载在a标签,存在动画复位问题)

/* Main Structure Styling */
.header {
    width: 100%;
}
.header ul {
    display: flex;
    gap: 20px;
    justify-items:center;
}
.header ul li {
    padding: 10px;
}
a {
    text-decoration: none;
    color: #000;
    font-size: 20px;
}
li {
    list-style: none;
}



  /* Dropdown Menu Styling*/
.dropdown-menu-hook {
    position: relative;
}
.dropdown-menu {
    width: 120px;
    height: auto;
    background-color: #fff;
    border-radius: 10px;
    padding: 1rem;
    position: absolute;
    top: 45.5px;
    left: 0;
    visibility: hidden;
    pointer-events: all;
    opacity: 0;
    color: #000;
    box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
}
.items {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}
.item1,
.item2,
.item3,
.item4 {
    line-height: 1.2;
    color:#000;
    font-weight: 500;
    padding: 10px;
    transition: .3s;
}
.dropdown-menu-hook:hover > .dropdown-menu {
    visibility: visible;
    pointer-events: all;
    opacity: .9;
}
.dropdown-menu {
    transition: opacity .5s;
}


/* Line Animation */
.line-anim {
    position: relative;
    width: fit-content;
}
.line-anim:after {
    content: "";
    background-color: #000;
    height: 1px;
    width: 0;
    left: 0px;
    top: -5px;
    transition: 0.4s ease-out;
    position: absolute;
}
.line-anim:hover:after {
    width: 100%;
}

a:hover > .line-anim {
  display: block;
}
<nav class="header">
    <ul>
        <li><a href="#" class="line-anim">ABOUT</a></li>
        <li class="dropdown-menu-hook">
          <a href="#" class="line-anim">TESTIMONIALS</a> 
            <div class="dropdown-menu">
                <div class="items">
                    <a class="item1" href="#">Item 1</a>
                    <a class="item2" href="#">Item 2</a>
                    <a class="item3" href="#">Item 3</a>
                    <a class="item4" href="#">Item 4</a>
                </div>
            </div>
        </li>
        <li ><a href="#" class="line-anim">CONTACT</a></li>
    </ul>
</nav>

已尝试无效写法

.line-anim:after {
    width: 100%!important;
    transition: all .9s;
}

参考效果版本(.line-anim挂载在li标签,效果符合预期但不符合结构要求)

/* Main Structure Styling */
.header {
    width: 100%;
}
.header ul {
    display: flex;
    gap: 20px;
    justify-items:center;
}
.header ul li {
    padding: 10px;
}
a {
    text-decoration: none;
    color: #000;
    font-size: 20px;
}
li {
    list-style: none;
}



  /* Dropdown Menu Styling*/
.dropdown-menu-hook {
    position: relative;
}
.dropdown-menu {
    width: 120px;
    height: auto;
    background-color: #fff;
    border-radius: 10px;
    padding: 1rem;
    position: absolute;
    top: 45.5px;
    left: 0;
    visibility: hidden;
    pointer-events: all;
    opacity: 0;
    color: #000;
    box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
}
.items {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}
.item1,
.item2,
.item3,
.item4 {
    line-height: 1.2;
    color:#000;
    font-weight: 500;
    padding: 10px;
    transition: .3s;
}
.dropdown-menu-hook:hover > .dropdown-menu {
    visibility: visible;
    pointer-events: all;
    opacity: .9;
}
.dropdown-menu {
    transition: opacity .5s;
}


/* Line Animation */
.line-anim {
    position: relative;
    width: fit-content;
}
.line-anim:after {
    content: "";
    background-color: #000;
    height: 1px;
    width: 0;
    left: 0px;
    top: -5px;
    transition: 0.4s ease-out;
    position: absolute;
}
.line-anim:hover:after {
    width: 100%;
}

a:hover > .line-anim {
  display: block;
}
<nav class="header">
    <ul>
        <li><a href="#" class="line-anim">ABOUT</a></li>
        <li class="dropdown-menu-hook line-anim">
          <a href="#" class="">TESTIMONIALS</a> 
            <div class="dropdown-menu">
                <div class="items">
                    <a class="item1" href="#">Item 1</a>
                    <a class="item2" href="#">Item 2</a>
                    <a class="item3" href="#">Item 3</a>
                    <a class="item4" href="#">Item 4</a>
                </div>
            </div>
        </li>
        <li ><a href="#" class="line-anim">CONTACT</a></li>
    </ul>
</nav>
解决方案

不需要移动.line-anim类的挂载位置,也不需要JavaScript,利用CSS后代选择器,在父级.dropdown-menu-hook处于hover状态时,选中内部的.line-anim::after伪元素设置宽度100%即可,原有过渡动画会完全保留。

只需要在原有CSS代码中新增一行规则:

/* 下拉父容器hover时,保持内部链接下划线激活状态 */
.dropdown-menu-hook:hover > .line-anim:after {
    width: 100%;
}

实现原理

  • 鼠标移到下拉菜单上时,hover状态是绑定在父级.dropdown-menu-hook(li元素)上的,不会因为鼠标离开内部a标签就丢失hover状态
  • 原代码只给.line-anim:hover:after设置了宽度100%,鼠标移到下拉菜单时离开了a标签,该选择器就不生效,导致下划线收回
  • 新增的选择器会在父li处于hover状态时,持续让内部a标签的下划线保持100%宽度,同时原有的transition属性正常生效,下划线展开、收回都会保持平滑动画,不会出现硬切效果
  • 普通导航项(ABOUT、CONTACT)没有.dropdown-menu-hook父级,不受该规则影响,原有交互逻辑保持不变

内容的提问来源于stack exchange,提问作者Maryando

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 07:27:14