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

导航列表超出容器范围,如何为多余项创建下拉菜单?

实现自适应横向导航栏:超出项自动移入下拉菜单

需求说明

现有一个包含<a>链接的<nav>元素,链接沿页面横向排列,但当区域宽度有限时,链接会超出容器范围(边框标识区域)。需要将溢出的链接自动移入一个类似开发者工具样式的下拉菜单中,保持导航栏整洁。

解决方案

我们将通过调整HTML结构、优化CSS布局,并配合JavaScript实现动态判断与元素移动,完成自适应导航栏的功能。

1. 修改HTML结构

添加链接容器、下拉按钮与下拉菜单,为动态调整提供基础结构:

<header class="doc-header">
  <div class="wrapper wrapper-border">
      <nav class="doc-nav">
        <!-- 包裹所有导航链接,用于计算宽度 -->
        <div class="nav-links-container">
          <a class="doc-page-link" href="yahoo.com">Ansible</a>
          <a class="doc-page-link" href="google.com">PowerShell</a>
          <a class="doc-page-link" href="example.com">Python</a>
          <a class="doc-page-link" href="example.com">JavaScript</a>
          <a class="doc-page-link" href="example.com">Docker</a>
        </div>
        <!-- 下拉触发按钮 -->
        <button class="nav-dropdown-btn" aria-expanded="false">
          <span>更多</span>
          <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
            <path d="M7 10l5 5 5-5z"/>
          </svg>
        </button>
        <!-- 溢出链接的下拉菜单 -->
        <ul class="nav-dropdown-menu"></ul>
      </nav>
  </div>
</header>

2. 优化SCSS样式

使用Flex布局实现自适应,添加下拉菜单的基础样式与交互效果:

// 保留原有的变量与基础样式
.wrapper {
  max-width: calc(#{$content-width} - #{$spacing-unit});
  margin-right: auto;
  margin-left: auto;
  padding-right: $spacing-unit * .5;
  padding-left: $spacing-unit * .5;
  @extend %clearfix;

  @media screen and (min-width: $on-large) {
    max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
    padding-right: $spacing-unit;
    padding-left: $spacing-unit;
  }
}

.wrapper-border {
  border: 1px solid $border-color-01;
  border-top: none;
}

.doc-nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  overflow: hidden;
  padding: 0.5rem 0;

  // 链接容器:负责承载可见链接
  .nav-links-container {
    display: flex;
    gap: 20px;
    flex: 1;
    overflow: hidden;
    min-width: 0; // 关键:确保flex子元素能收缩到内容宽度以下
  }

  // 导航链接样式
  .doc-page-link {
    color: $text-color;
    line-height: $base-line-height;
    white-space: nowrap; // 禁止链接换行
    text-decoration: none;

    &:hover {
      text-decoration: underline;
    }
  }

  // 下拉按钮:默认隐藏,溢出时显示
  .nav-dropdown-btn {
    display: none;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.6rem;
    border: none;
    background: transparent;
    color: $text-color;
    cursor: pointer;
    border-radius: 4px;
    transition: background-color 0.2s;

    &:hover {
      background-color: rgba(0,0,0,0.05);
    }

    svg {
      transition: transform 0.2s;
    }

    &[aria-expanded="true"] svg {
      transform: rotate(180deg);
    }
  }

  // 下拉菜单:默认隐藏,点击按钮显示
  .nav-dropdown-menu {
    position: absolute;
    top: calc(100% + 0.3rem);
    right: 0;
    list-style: none;
    margin: 0;
    padding: 0.5rem 0;
    background-color: white;
    border: 1px solid $border-color-01;
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    display: none;
    min-width: 120px;
    z-index: 100;

    &.show {
      display: block;
    }

    a {
      display: block;
      padding: 0.5rem 1rem;
      color: $text-color;
      text-decoration: none;
      white-space: nowrap;
      transition: background-color 0.2s;

      &:hover {
        background-color: rgba(0,0,0,0.05);
      }
    }
  }
}

3. JavaScript动态逻辑

实现链接溢出判断、元素移动与下拉交互:

document.addEventListener('DOMContentLoaded', initNavDropdown);
window.addEventListener('resize', initNavDropdown);

function initNavDropdown() {
  const nav = document.querySelector('.doc-nav');
  const linksContainer = nav.querySelector('.nav-links-container');
  const dropdownBtn = nav.querySelector('.nav-dropdown-btn');
  const dropdownMenu = nav.querySelector('.nav-dropdown-menu');
  const allLinks = [...linksContainer.querySelectorAll('.doc-page-link')];

  // 重置状态:把所有链接放回原容器,隐藏下拉菜单
  dropdownMenu.querySelectorAll('a').forEach(link => linksContainer.appendChild(link));
  dropdownBtn.style.display = 'none';
  dropdownMenu.classList.remove('show');
  dropdownBtn.setAttribute('aria-expanded', 'false');

  // 计算容器宽度与链接总宽度,筛选溢出项
  const containerWidth = linksContainer.clientWidth;
  let totalWidth = 0;
  const overflowLinks = [];

  allLinks.forEach(link => {
    totalWidth += link.offsetWidth + parseInt(getComputedStyle(link).marginRight);
    if (totalWidth > containerWidth) {
      overflowLinks.push(link);
    }
  });

  // 处理溢出链接:移入下拉菜单并显示按钮
  if (overflowLinks.length > 0) {
    dropdownBtn.style.display = 'flex';
    overflowLinks.forEach(link => {
      const li = document.createElement('li');
      li.appendChild(link);
      dropdownMenu.appendChild(li);
    });
  }

  // 下拉按钮点击交互
  dropdownBtn.addEventListener('click', () => {
    const isExpanded = dropdownBtn.getAttribute('aria-expanded') === 'true';
    dropdownBtn.setAttribute('aria-expanded', !isExpanded);
    dropdownMenu.classList.toggle('show', !isExpanded);
  });

  // 点击外部关闭下拉菜单
  document.addEventListener('click', (e) => {
    if (!nav.contains(e.target)) {
      dropdownBtn.setAttribute('aria-expanded', 'false');
      dropdownMenu.classList.remove('show');
    }
  });
}

关键说明

  • Flex布局适配:通过flex:1让链接容器占据剩余空间,min-width:0解决Flex子元素无法收缩的问题。
  • 动态判断逻辑:通过计算容器与链接的宽度差,精准筛选溢出项,确保导航栏始终适配容器宽度。
  • 交互体验优化:添加按钮悬停、菜单展开动画,以及点击外部关闭菜单的逻辑,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 22:37:13