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

如何实现移动端或浏览器缩放时点击侧边栏外部关闭菜单

移动端点击侧边栏外部关闭菜单的解决方案

问题描述

移动端视图下打开侧边栏菜单后,点击菜单外部区域无法关闭菜单,需通过JavaScript实现该交互。

解决方案步骤

1. 添加遮罩层(优化交互体验)

在HTML的.sidebar元素后新增一个遮罩层,用于捕获外部点击事件:

<div class="sidebar">
  <!-- 原有侧边栏内容 -->
</div>
<!-- 新增遮罩层 -->
<div class="sidebar-overlay"></div>

2. 更新CSS样式

为遮罩层添加基础样式,并设置仅在移动端菜单打开时显示:

/* 遮罩层基础样式 */
.sidebar-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0,0,0,0.5);
  z-index: 80;
  display: none;
}

/* 移动端下,菜单打开时显示遮罩 */
@media (max-width: 480px) {
  #menu-toggle:checked ~ .sidebar-overlay {
    display: block;
  }
}

3. 添加JavaScript逻辑

在</body>标签前插入以下代码,实现点击外部关闭菜单的逻辑:

const menuToggle = document.getElementById('menu-toggle');
const sidebar = document.querySelector('.sidebar');
const menuButton = document.querySelector('label[for="menu-toggle"]');

// 点击遮罩或菜单外部关闭菜单
document.addEventListener('click', function(e) {
  // 仅在移动端且菜单已打开时处理
  if (window.innerWidth <= 480 && menuToggle.checked) {
    const isClickInsideSidebar = sidebar.contains(e.target);
    const isClickOnMenuButton = menuButton.contains(e.target);
    
    if (!isClickInsideSidebar && !isClickOnMenuButton) {
      menuToggle.checked = false;
    }
  }
});

// 窗口大小变化时重置菜单状态
window.addEventListener('resize', function() {
  if (window.innerWidth > 480) {
    menuToggle.checked = false;
  }
});

完整修改后代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
  <title>Admin Dashboard | Keyframe Effects</title>
  <link rel="stylesheet" href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css">
  <style>
    @import url('https://fonts.googleapis.com/css2?family=Merriweather+Sans:wght@300;400;500;600&display=swap');
    :root {
      --main-color: #34425A;
      --color-dark: #34425A;
      --text-grey: #B0B0B0;
    }
    
    * {
      margin: 0;
      padding: 0;
      text-decoration: none;
      list-style-type: none;
      box-sizing: border-box;
      font-family: 'Merriweather', sans-serif;
    }
    
    #menu-toggle {
      display: none;
    }
    
    .sidebar {
      position: fixed;
      height: 100%;
      width: 165px;
      left: 0;
      bottom: 0;
      top: 0;
      z-index: 100;
      background-color: #d6eaf8;
      transition: left 300ms;
    }
    
    .side-menu ul li:hover {
      background-color: #1b4f72;
    }
    
    .side-menu a {
      color: #555;
      font-size: 1rem;
    }
    
    .side-menu a:hover {
      color: #fff;
    }
    
    .side-header {
      background-color: #2471a3;
      height: 60px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .side-header h3,
    .side-header span {
      color: #fff;
      font-weight: 400;
    }
    
    .side-content {
      padding: 2rem 0rem;
      height: calc(100vh - 60px);
      overflow: auto;
    }
    
    .side-content::-webkit-scrollbar {
      width: 5px;
    }
    
    .side-content::-webkit-scrollbar-track {
      box-shadow: inset 0 0 5px grey;
      border-radius: 10px;
    }
    
    .side-content::-webkit-scrollbar-thumb {
      background: #b0b0b0;
      border-radius: 10px;
    }
    
    .side-content::-webkit-scrollbar-thumb:hover {
      background: #b30000;
    }
    
    .side-menu ul {
      text-align: center;
    }
    
    .side-menu a {
      display: block;
      padding: 1rem 0rem;
      text-align: left;
    }
    
    .side-menu a.active {
      background: #1b4f72;
      border-bottom: solid 1px #d6eaf8;
    }
    
    .side-menu a.active span,
    .side-menu a.active small {
      color: #fff;
    }
    
    .side-menu a span {
      min-height: 15px;
      display: inline-flex;
      align-items: center;
      font-size: 1.7rem;
      margin: 0rem 0.5rem 0rem 0.5rem !important;
    }
    
    .side-menu a small {
      font-size: 0.95rem;
      font-weight: normal;
      margin: 0rem 1rem 0rem 1rem !important;
      font-family: 'Open Sans', sans-serif;
    }
    
    #menu-toggle:checked~.sidebar {
      width: 60px;
    }
    
    #menu-toggle:checked~.sidebar .side-header span {
      display: none;
    }
    
    #menu-toggle:checked~.main-content {
      margin-left: 60px;
      width: calc(100% - 60px);
    }
    
    #menu-toggle:checked~.main-content header {
      left: 60px;
    }
    
    #menu-toggle:checked~.sidebar .profile,
    #menu-toggle:checked~.sidebar .side-menu a small {
      display: none;
    }
    
    #menu-toggle:checked~.sidebar .side-menu a {
      font-size: 1.3rem;
      padding: 0.7rem 0rem 0.7rem 0rem;
      text-align: center;
    }
    
    #menu-toggle:checked~.sidebar .side-menu a span {
      font-size: 1.3rem;
    }
    
    .main-content {
      margin-left: 165px;
      width: calc(100% - 165px);
      transition: margin-left 300ms;
    }
    
    header {
      position: fixed;
      right: 0;
      top: 0;
      left: 165px;
      z-index: 100;
      height: 60px;
      box-shadow: 0px 5px 5px -5px rgb(0 0 0 /10%);
      background-color: #2471a3;
      transition: left 300ms;
    }
    
    .header-content,
    .header-menu {
      display: flex;
      align-items: center;
    }
    
    .header-content {
      justify-content: space-between;
      padding: 1rem 1rem;
      color: #fff!important;
    }
    
    .header-content label:first-child span {
      font-size: 1.3rem;
    }
    
    .header-content label {
      cursor: pointer;
    }
    
    .header-menu {
      justify-content: flex-end;
      padding-top: .5rem;
    }
    
    .page-content {
      height: 100vh;
      padding: 1.3rem 1rem;
      background: #f1f4f9;
    }
    
    /* 新增遮罩层样式 */
    .sidebar-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(0,0,0,0.5);
      z-index: 80;
      display: none;
    }
    
    @media (min-width: 320px) and (max-width: 480px) {
      .sidebar {
        left: -165px;
        z-index: 90;
      }
      header {
        left: 0;
        width: 100%;
      }
      .main-content {
        margin-left: 0;
        width: 100%;
      }
      #menu-toggle:checked~.sidebar {
        left: 0;
        width: 165px;
      }
      #menu-toggle:checked~.sidebar .side-header span {
        display: inline-block;
      }
      #menu-toggle:checked~.sidebar .profile,
      #menu-toggle:checked~.sidebar .side-menu a small {
        display: block;
      }
      #menu-toggle:checked~.sidebar .side-menu a span {
        font-size: 1.7rem;
      }
      #menu-toggle:checked~.main-content header {
        left: 0px;
      }
      /* 移动端菜单打开时显示遮罩 */
      #menu-toggle:checked ~ .sidebar-overlay {
        display: block;
      }
    }
  </style>
</head>
<body>
  <input type="checkbox" id="menu-toggle">
  <div class="sidebar">
    <div class="side-header">
      <h3>L<span>ogo</span></h3>
    </div>
    <div class="side-content">
      <div class="side-menu">
        <ul>
          <li>
            <a href="" class="active">
              <span class="las la-home"><small>Dashboard</small></span>
            </a>
          </li>
          <li>
            <a href="">
              <span class="las la-user-alt"><small>Profile</small></span>
            </a>
          </li>
          <li>
            <a href="">
              <span class="las la-envelope"><small>Mailbox</small></span>
            </a>
          </li>
          <li>
            <a href="">
              <span class="las la-clipboard-list"><small>Projects</small></span>
            </a>
          </li>
          <li>
            <a href="">
              <span class="las la-shopping-cart"><small>Orders</small></span>
            </a>
          </li>
          <li>
            <a href="">
              <span class="las la-tasks"><small>Tasks</small></span>
            </a>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <!-- 新增遮罩层 -->
  <div class="sidebar-overlay"></div>

  <div class="main-content">
    <header>
      <div class="header-content">
        <label for="menu-toggle">
          <span class="las la-bars"></span>
        </label>
      </div>
    </header>
    <main>
      <div class="page-header">
        <h1>Dashboard</h1>
        <small>Home / Dashboard</small>
      </div>
      <div class="page-content">
        Page content goes here...
      </div>
    </main>
  </div>

  <script>
    const menuToggle = document.getElementById('menu-toggle');
    const sidebar = document.querySelector('.sidebar');
    const menuButton = document.querySelector('label[for="menu-toggle"]');

    // 点击遮罩或菜单外部关闭菜单
    document.addEventListener('click', function(e) {
      if (window.innerWidth <= 480 && menuToggle.checked) {
        const isClickInsideSidebar = sidebar.contains(e.target);
        const isClickOnMenuButton = menuButton.contains(e.target);
        
        if (!isClickInsideSidebar && !isClickOnMenuButton) {
          menuToggle.checked = false;
        }
      }
    });

    // 窗口大小变化时重置菜单状态
    window.addEventListener('resize', function() {
      if (window.innerWidth > 480) {
        menuToggle.checked = false;
      }
    });
  </script>
</body>
</html>

逻辑说明

  • 遮罩层在移动端菜单打开时显示,既增强视觉遮挡反馈,又能捕获外部点击事件
  • JS监听全局点击,判断点击区域是否属于侧边栏或菜单按钮,非目标区域则关闭菜单
  • 窗口尺寸变化时自动重置菜单状态,避免移动端切换到桌面端后菜单状态异常

内容的提问来源于stack exchange,提问作者Eng Khalid Abdi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:14:50