el-menu在作为导航时非常好用,有非常多的使用场景,但是这个组件本身有一些问题,在使用时需要额外注意:
一、el-menu多次修改default-active时,视图不更新Bug:
解决办法很简单:
// 不高亮任何地方 如果写成固定值,则第二次执行该语句时el-menu不会更新视图 curNavActive.value = Math.random().toString() // 高亮指定index的el-menu-item 如果不这样写,则第二次执行该语句时el-menu不会更新视图 curNavActive.value = '' nextTick(() => { curNavActive.value = '知识库' })
复制
二、el-menu-item点击事件会冒泡到el-sub-menu的Bug:
el-menu同时给`el-sub-menu`和下面的`el-menu-item`绑定事件时,点击el-menu-item,事件会冒泡到el-sub-menu中,而且el-menu-item的click事件在组件内部做了处理,不会返回event事件的参数,所以无法使用事件修饰符直接阻止冒泡事件,只能在el-sub-menu的事件中判断:
const subMenu_clickEvent = ($event: MouseEvent) => { // el-menu-item事件会冒泡到这里 如果点击的文件夹下面的item 不要执行任何操作 let subMenuTitle = document.querySelector('.subMenuTitle') if ($event.target !== subMenuTitle) { return } else { // 真实el-sub-menu应该执行的事件 ... } }
复制
三、el-menu阻止el-sub-menu的展开和折叠事件:
不要把事件挂载在 el-sub-menu本身上,而是要挂载在其 title插槽中,并且阻止其事件冒泡即可!
<el-sub-menu index="知识库" > <template #title> <!-- 在 title插槽中绑定一个事件 并阻止其冒泡,这样在点击这个区域时, el-sub-menu就不会展开/折叠了 --> <el-icon><Collection /></el-icon> <div class="subMenuTitle" @click.stop="switchToLibHome"> 中医知识库 </div> </template> <el-menu-item v-for="item in $knStore.curSourceList" :key="item.id" :index="item.id + '知识源'" @click="switchViewFile(item)" > <span>{{ item.name }}</span> </el-menu-item> </el-sub-menu>
复制
在 title插槽中,使用一个足够大的div来包裹里面的内容(注意不要包裹icon图标),这样用户只能通过点击这个 div 之外的部分来展开和折叠 sub-menu。
就相当于阻止了sub-menu自带的展开/折叠事件。
设置 width: 100% 和 flex-grow:1 都行,但是建议设置为width: 100% ,而且不要懒省劲用style设置行内样式,因为这个地方比较特殊,它的样式优先级比较低才好。
不把事件挂载到el-sub-menu本身还有一个好处,现在子菜单下面的子节点的事件不会再冒泡到el-sub-menu上了。也就无需判断 event.target了。
但是这样一来,el-menu就不能进行收起折叠了,因为这个div有宽度相关的样式,所以就会出现el-sub-menu没有正常折叠的bug:
如果el-menu不需要收起,那么到这里就算结束了,展开情况下菜单使用很正常,但是如果还想对收起状态进行处理,那就需要在el-menu菜单收起时,控制其标签样式了:
1、首先,将div更换为span标签(这很重要!),并设置css样式为 width: 100% 。
<el-sub-menu index="知识库" expand-close-icon="CaretBottom" expand-open-icon="CaretTop" > <template #title> <!-- icon图标一定一定要写在span外面,否则el-menu收起时会没有图标! --> <el-icon><Collection /></el-icon> <!-- div标签也一定要改为span标签! --> <span class="subMenuTitle" @click.stop="switchToLibHome"> 中医知识库 </span> <!-- <div class="subMenuTitle" @click.stop="switchToLibHome"> 中医知识库 </div> --> </template> ...... </el-sub-menu>
复制
2、然后在el-menu整体收起时,用js获取这个标签,并将这个标签的width设置为0即可。
但是在研究后发现,其实elementUI内部在el-menu收起时已经将内部所有span标签的宽度设置为0了,只不过优先级没有我们设置的优先级高,所以没有生效。
也就是说,我们只需要降低span标签的 width:100% css样式的优先级即可,我是通过移动css代码的位置来降低其优先级的,反正只要优先级比elementUI的低就可以:
这样就不需要js去控制css样式了,更简单方便。
最后给这个span标签加上超长省略样式:
// el-menu子菜单样式(不要移动它的位置,要保持其css的低优先级,否则el-menu收起时会出现占位问题) .subMenuTitle { width: 100%; // 超长文本截断 overflow: hidden; text-overflow: ellipsis; }
复制
现在好像一切都可以了,但是我们还需要做最后一步:
给 sub-menu的小图标也注册个点击事件,防止在el-menu收起时,点击图标无效的情况发生。
<template #title> <!-- 给小图标也注册点击事件,不然收起时点不到下面的span标签 --> <el-icon @click.stop="switchToLibHome"><Collection /></el-icon> <span class="subMenuTitle" @click.stop="switchToLibHome"> {{ $knStore.curLib?.name }} </span> </template>
复制
最后的效果很完美:
四、el-sub-menu子项过多,折叠时无法完全显示Bug:
可以看到,在收起状态下,el-sub-menu的弹窗即使上下两端都顶到了头,也没显示完全,下面还有1个元素。
而这个el-sub-menu的弹窗默认是在body中插入的,我在Vue子页面中不能只通过css去操控body中的元素。
所以就需要使用js来操控其样式了。
1、先给popper弹窗加上一个自定义样式:
<el-sub-menu index="..." popper-class="subMenuPopper_knowledgeLib" > ... </el-sub-menu>
复制
2、再在折叠el-menu之后使用js去操控submenu的弹窗样式
// 收起el-menu const foldNav = () => { isCollapse.value = true navWidth.value = 5.5 // 折叠后 给知识源列表弹窗添加样式 避免知识源文件过多时弹窗无法完全显示所有文件列表 setTimeout(() => { let subMenuPopper: HTMLElement | null = document.body.querySelector( '.subMenuPopper_knowledgeLib' ) if (subMenuPopper) { subMenuPopper.style.maxHeight = '80vh' subMenuPopper.style.overflow = 'auto' } }, 2000) }
复制
这里不知道为什么使用 nextTick 始终获取不到这个popper元素,可能是这个元素的渲染比较特殊吧,只能是使用定时器来获取,1秒还是2秒都可以。
更改样式之后的效果: