blog icon indicating copy to clipboard operation
blog copied to clipboard

后台系统 Layout 实现总结

Open ryancui92 opened this issue 7 years ago • 0 comments

自己写布局

最近要用 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. 一开始用了 mouseovermouseout,在 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 那样的悬浮菜单

image

自然的想法是跟 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 看看:

李猜猜的 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

多级菜单

只要解决几个问题就行了

  1. 下拉菜单 expand 时,正确计算需要的 max-height 值(下拉菜单里面可能还有下拉菜单)
  2. 下拉菜单 expand 时,要把同级的其他菜单 collapse 掉
  3. 每下降一个 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

ryancui92 avatar Jul 19 '17 03:07 ryancui92