blog
blog copied to clipboard
后台系统 Layout 实现总结
自己写布局
最近要用 Angular 搞一个后台系统的框架,就遇到了这个问题,在 Github 上搜到的解决方案基本都集成了自己的一套 UI Component,但我特么只想要个 Layout 啊!组件我不用你的啊,我用 PrimeNG 啊!能不能不要搞个 CSS Confliction 啊!由于这些方案都太大太杂(~~而且不想再依赖 jQuery 了~~),所以还是自己写一个。
- 包含 Header、Footer、Sidebar、Content 四部分的布局
- 四部分自适应浏览器宽高,不考虑移动端,不搞响应式
- Sidebar 固定宽度,Content 自适应
- Header 有 Dropdown Menu,可以在那里做退出等操作
- Sidebar 包含一个侧边栏菜单,菜单要有下滑动画
- 使用原生 JavaScript 实现,不依赖任何 JS 库、组件
- 不依赖任何 CSS 框架
需求其实不是很复杂,而且我也找了几个网站来参考,包括 AdminLTE 和阿里云的后台,主要参考阿里云的后台。
问题总结
position: absolute? fixed?
全部用 absolute 布局,对 absolute 的布局原点要清楚,否则 top/bottom 就会搞错
absolute 的参考原点是有 absolute/fixed 布局的最近父元素 fixed 的参考原点是浏览器
mouseover or mouseenter
在实现 Header Dropdown Menu 的时候,需要 hover 到 btn 就 display 菜单,并且在移进 menu panel 的时候维持这个 display. 一开始用了 mouseover
跟 mouseout
,在 btn 和 panel 都做两次检测。
$dropdownBtns[i].addEventListener('mouseover', function (evt) {
var ele = evt.target
var panel = ele.nextElementSibling
panel.style.visibility = 'visible'
})
$dropdownBtns[i].addEventListener('mouseout', function (evt) {
var ele = evt.target
var panel = ele.nextElementSibling
panel.style.visibility = 'hidden'
})
$dropdownPanels[i].addEventListener('mouseover', function (evt) {
var ele = evt.target
ele.style.visibility = 'visible'
})
$dropdownPanels[i].addEventListener('mouseout', function (evt) {
var ele = evt.target
ele.style.visibility = 'hidden'
})
当我的 menu 还是一个 div 的时候,一切都很完美。Nice!
当在这个 div 内部填东西的时候就懵逼了,在里面随便动动里面的内容就会突然消失。。
不论鼠标指针穿过被选元素或其子元素,都会触发
mouseover
事件。对应mouseout
只有在鼠标指针穿过被选元素时,才会触发mouseenter
事件。对应mouseleave
overflow: visible
在实现侧边栏菜单的时候,一开始想要实现像 AdminLTE 那样的悬浮菜单
自然的想法是跟 dropdown menu 一个道理,只是 dropdown panel 不定位在下面了,定在右边,于是大概就是这样
<div class="menu">
<div class="btn">Menu</div>
<div class="panel">Panel</div>
</div>
.menu {
position: relative;
width: 100px;
height: 200px;
}
.btn {
position: relative;
width: 100px;
height: 20px;
}
.panel {
position: absolute;
top: 0;
left: 100px;
}
没有问题,布局是正确的。panel 的确出现在了右边,加上一些 js 就搞定了!但既然这是个 menu,那应该会有很多个 item 吧,那我加个 overflow 吧
.menu {
overflow: auto;
}
What the fvck! 怎么 x 也有个滚动条,我要你这个 panel 突出去啊,改一下
.menu {
overflow-y: auto;
}
还是不行,查了下,貌似有个 visible
属性,允许子元素显示在容器外面,很好!
.menu {
overflow-y: auto;
overflow-x: visible;
}
这个滚动条还在,嗯。
原来这个 visible
是没用的!如果对 x 或 y 单独使用 visible
,另外一个轴没有设置 visible
,这个设置成 visible
的轴会自动转成 auto
。
给个大牛李猜猜的 CodePen 看看:
transition height/max-height
到了实现侧边栏动态伸缩的时候,第一时间写下了这样的 transition
.sidebar {
transition: width 0.5s ease-out;
}
绑定一下 button 的 click 事件,把 style 的 width 改一下,效果就出来了,很好。
那就再把下拉菜单的动画效果也做了吧
.submenu-box {
transition: height 0.5s ease-out;
}
果不其然,这样又 fail 了,要换成 max-height
才行。
看这里 how-can-i-transition-height-0-to-height-auto-using-css
多级菜单
只要解决几个问题就行了
- 下拉菜单 expand 时,正确计算需要的
max-height
值(下拉菜单里面可能还有下拉菜单) - 下拉菜单 expand 时,要把同级的其他菜单 collapse 掉
- 每下降一个 level,padding 要增加,显示缩进的效果
对于第三点,Less 的循环就能很好的解决了,参考官网的例子,生成 6 层,每层 padding 递增 10px
.generate-menu-item(6);
.generate-menu-item(@n, @i: 1) when (@i =< @n) {
.menu-item-@{i} {
padding-left: 10px * @i;
}
.generate-menu-item(@n, (@i + 1));
}
第二点折衷成只在第一层菜单做 single expand,下面的层就不管了,爱 expand 多少就 expand 多少,这样考虑的话,每次处理 expand/collapse 事件时,只需要处理其他的 top menu 就可以了。
计算后代节点有多少个 menu-item 就更简单了,直接在菜单中 getElementsByClassName
就行了,每个菜单项都有一个固定的 menu-item
class,就能直接算出来了。
总结
作为一名前端小白,做这样一个布局都踩到不少基础的坑,包括事件代理这里面也还没实现,但不得不说,StackOverFlow 简直是神器!
最后附上项目地址:admin-layout