Element UI菜单折叠后的el-menu-item属性无法修改问题解决

要理解为什么"挂 body 上就可以",你必须知道两件事:

CSS 变量(--xxx)是"继承型属性"

CSS 变量的继承规则:

  • 子元素可以继承父元素的 CSS 变量

  • 只能继承祖先节点的变量

  • 如果祖先链上没有这个变量 → 变量无效

举例:

复制代码
<div style="--color: red">
  <span>能继承</span>
</div>

但如果结构是:

复制代码
<div style="--color: red"></div>

<body>
  <span>不能继承</span>
</body>

span 不会继承 div 的变量,因为它不是 div 的子孙节点。

折叠后 ElementUI 会把菜单弹窗挂到 body:

复制代码
<body>
  <ul class="el-menu el-menu--popup-right-start">
    <li class="el-menu-item">...</li>
  </ul>
</body>

注意:

  • popup 不在你的组件内部

  • popup 不是 .sidebar-menu 的子元素

  • popup 不在 scoped 样式作用域内

所以:

❌ 你写在组件上的 CSS 变量,popup 根本继承不到

❌ 你写在 scoped 样式里的 hover,popup 根本命不中

❌ v-bind() 在全局样式里无效

这就是你之前所有方法都失败的原因。

那为什么挂到 body 上就可以?

因为 popup 是 body 的子元素:

复制代码
body
 └── ul.el-menu--popup

所以当你这样写:

复制代码
document.body.style.setProperty('--hover-color', this.hoverColor)

popup 就能继承:

复制代码
body
 └── popup(继承 --hover-color)

然后你在全局样式里写:

复制代码
body .el-menu--popup .el-menu-item:hover {
  background-color: var(--hover-color) !important;
}

popup 就能正确使用 hoverColor。

一句话总结原理

因为 popup 不在你的组件里,所以它继承不到组件上的 CSS 变量。 但 popup 是 body 的子元素,所以把 CSS 变量挂到 body 上,它就能继承了。

再补一句你会恍然大悟的话

你之前写的:

复制代码
<div class="sidebar-menu" :style="{ '--hover-color': hoverColor }">

对 popup 来说等价于:

复制代码
一个完全无关的 div 上设置了变量
popup 根本看不到

所以当然不生效。

代码示例

复制代码
<template>
  <div class="sidebar-menu">
    <!-- 顶部 logo 区域 -->
    <div class="menu-header">
      <el-menu
        :default-active="$route.path"
        class="el-menu-vertical-demo"
        router
        :collapse="isCollapse"
        :text-color="textColor"
        :active-text-color="activeColor"
        :background-color="backgroundColor"
      >
        <el-menu-item disabled>
          <div class="logo">
            <img src="@/assets/images/horse.png" />
          </div>
          <span :style="{ color: textColor }">后台管理系统</span>
        </el-menu-item>
      </el-menu>
    </div>

    <!-- 菜单项区域 -->
    <div class="menu-scroll">
      <el-menu
        v-if="isLightOn"
        :default-active="$route.path"
        class="el-menu-vertical-demo"
        router
        :collapse="isCollapse"
        :text-color="textColor"
        :active-text-color="activeColor"
      >
        <menu-item
          v-for="item in menu_list"
          :key="item.path"
          :item="item"
          @menu-click="updateMenu"
        />
      </el-menu>
      <el-menu
        v-else
        :default-active="$route.path"
        class="el-menu-vertical-demo"
        router
        :collapse="isCollapse"
        :text-color="textColor"
        :active-text-color="activeColor"
        :background-color="backgroundColor"
      >
        <menu-item
          v-for="item in menu_list"
          :key="item.path"
          :item="item"
          @menu-click="updateMenu"
        />
      </el-menu>
    </div>
  </div>
</template>

<script>
import MenuItem from "@/components/Layout/MenuItem.vue";
import { mapActions, mapGetters } from "vuex";

export default {
  name: "SidebarMenu",
  components: { MenuItem },
  props: ["isCollapse"],
  computed: {
    ...mapGetters("PermissionModule", ["menuPermissions"]),
    ...mapGetters("LightModule", [
      "isLightOn",
      "backgroundColor",
      "textColor",
      "activeColor",
      "hoverColor",
    ]),
    menu_list() {
      return this.menuPermissions || [];
    },
  },
  mounted() {
    document.body.style.setProperty("--hover-color", this.hoverColor);
    this.updateMenuFromRoute(this.$route);
  },
  watch: {
    hoverColor(val) {
      document.body.style.setProperty("--hover-color", val);
    },
  },
  methods: {
    ...mapActions("MenuStateModule", ["setCurrentMenuaddTabMenu"]),
    updateMenuFromRoute(route) {
      this.setCurrentMenuaddTabMenu(route.path);
    },
    updateMenu(path) {
      this.setCurrentMenuaddTabMenu(path);
    },
  },
};
</script>

<style lang="less" scoped>
/* ===========================
   外层统一背景(主题切换核心)
   =========================== */
.sidebar-menu {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: v-bind(backgroundColor);

  /* 顶部区域同步背景 */
  .menu-header {
    flex-shrink: 0;
    position: sticky;
    top: 0;
    z-index: 10;
    background-color: v-bind(backgroundColor);

    ::v-deep .el-menu-item.is-disabled {
      opacity: 1;
      cursor: pointer;
      background-color: transparent !important;
      color: inherit !important;

      &:hover {
        background-color: transparent !important;
      }
    }
  }

  /* ===========================
     菜单滚动区域
     =========================== */
  .menu-scroll {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    background-color: v-bind(backgroundColor);

    ::v-deep .el-menu,
    ::v-deep .el-submenu__title,
    ::v-deep .el-menu-item {
      background-color: transparent !important;
    }

    ::v-deep .el-menu-item:hover,
    ::v-deep .el-submenu__title:hover {
      background-color: v-bind(hoverColor) !important;
    }

    &::-webkit-scrollbar {
      width: 6px;
    }
    &::-webkit-scrollbar-thumb {
      background-color: #ccc;
      border-radius: 3px;
    }
    &::-webkit-scrollbar-track {
      background-color: #f5f5f5;
    }
  }
}

/* logo 样式 */
.logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: white;
  border-radius: 50%;
  margin-right: 8px;

  img {
    width: 30px;
    height: 30px;
    object-fit: cover;
  }
}

.el-menu-vertical-demo:not(.el-menu--collapse) {
  width: 200px;
}
</style>

<style lang="less">
/* popup 根节点背景 */
// body .el-menu--popup,
// body .el-menu--popup-right-start {
//   background-color: var(--bg-color) !important;
// }

/* popup hover(关键) */
/* 折叠 popup hover */
body .el-menu--popup .el-menu-item:hover,
body .el-menu--popup-right-start .el-menu-item:hover {
  background-color: var(--hover-color) !important;
  // color: var(--active-color) !important;
}
</style>
相关推荐
夏幻灵11 小时前
CSS三大特性:层叠、继承与优先级解析
前端·css
小杨同学呀呀呀呀12 小时前
Ant Design Vue <a-timeline>时间轴组件失效解决方案
前端·javascript·vue.js·typescript·anti-design-vue
华玥作者20 小时前
[特殊字符] VitePress 对接 Algolia AI 问答(DocSearch + AI Search)完整实战(下)
前端·人工智能·ai
Mr Xu_20 小时前
告别冗长 switch-case:Vue 项目中基于映射表的优雅路由数据匹配方案
前端·javascript·vue.js
前端摸鱼匠20 小时前
Vue 3 的toRefs保持响应性:讲解toRefs在解构响应式对象时的作用
前端·javascript·vue.js·前端框架·ecmascript
lang2015092821 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
好家伙VCC21 小时前
### WebRTC技术:实时通信的革新与实现####webRTC(Web Real-TimeComm
java·前端·python·webrtc
未来之窗软件服务1 天前
未来之窗昭和仙君(六十五)Vue与跨地区多部门开发—东方仙盟练气
前端·javascript·vue.js·仙盟创梦ide·东方仙盟·昭和仙君
嘿起屁儿整1 天前
面试点(网络层面)
前端·网络
VT.馒头1 天前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript