阅读 795

用套路写页面(第二节)不定高边栏,子元素高度动态且相邻联动

目录:

  • 5、左侧边栏(难度:3.5颗星)
  • 5.1、原型分析
  • 套路5:当高度/宽度为动态获取,且相邻联动的两个区域
  • 5.2、左边栏上下区域划分
  • 套路6:无痕滚动
  • 5.3、按钮的 CSS
  • 5.4、历史记录
  • 5.5、总结

5、左侧边栏(难度:3.5颗星)

前文参照:

手把手教你用套路写页面(HTML、CSS初中级前端教学)(第一节):juejin.im/post/684490…

5.1、原型分析

我们先看原型图:

分析:

  • 整体结构为上下两部分;
  • 上方由不定数量的单行按钮组成,从上到下延伸(因此需要考虑如果数量比较多,超出显示区域怎么办);
  • 下方由不定数量个历史操作组成;

总体考虑,我认为,这个布局有两种方法。

第一种(简单版):

  • 上下期中一个高度是固定的,另一个自适应(通常来说,下最大高度不高,所以下应是固定的,上自适应);
  • 自适应部分 overflow-y: auto

第二种(困难版):

  • 上不定高;
  • 下不定高但有最大高度限制;
  • 上的最大高度根据下的最大高度变化;
  • 上超出最大高度时,出现滚动条(或可以拖动滑动);
  • 下超出最大高度时,只显示最新的;

我们采用第二种方式进行开发(第一种太简单)。

第二种情况,纯css通常是无法解决的,因此我们需要js介入。

注:虽说flex可以,flex在某些老版本浏览器(非IE678)或者手机浏览器,比如IOS10以前,是可能出现bug的。我之前在阿里时,接到过来自客户反馈。因此不采用flex。

下方历史记录的代码思路:

  1. 历史记录整体为绝对定位,bottom: 0; width: 100%
  2. 设置下方的最大高度,最大高度为 h_max = dear的高度 + 单行历史记录的高度 * 最大显示历史记录的个数
  3. 下方理论高度为 h_count = dear的高度 + 单行历史记录的高度 * 历史数目个数
  4. 实际高度为 h = h_count > h_max ? h_max : h_count
  5. 下方默认有一条,当实际没有的时候,显示为【无】(占位使用);

上方按钮的代码思路:

  1. 绝对定位,top: 0
  2. 拿到下方的实际高度 h, 设置自己的 bottom 的值为 h;
  3. 设置 overflow-y: auto

套路5:当高度/宽度为动态获取,且相邻联动的两个区域

这是一个常见场景,通常见于其中一个区域数据来自于后端,另外一个区域填充以配合前者。

场景:

  • 父容器不定宽不定高(但确定可以容纳两个子元素);
  • 两个子元素共同填满父容器,且左右/上下相邻(为了方便分析,这里假设其上下相邻);
  • 【假设上下相邻】;
  • 两个子元素都不定高,但其中一个有最大高度(主次之分);
  • 有最大高度的子元素,其高度是动态获取的;

解决思路:

方法一(简单,但在极少数情况下会出现兼容性问题):

  • 假设两个子元素上下相邻,高度弹性;
  • 采用弹性布局: flex;
  • 配置子元素的弹性方向: flex-direction;
  • 两个子元素都不设置高度,但宽度为100%;
  • 设置有最大高度限制的子元素,其最大高度:max-height;
  • 设置无最大高度限制的子元素(即另一个子元素),获得全部的剩余弹性空间分配: flex-grow;
  • 于是受高度限制的子元素,其高度被填充内容的高度撑开;
  • 而剩下的区域,被另外一个子元素拿走;

方法二(复杂,可靠,可配置性强):

  • 假设两个子元素上下相邻,高度弹性;
  • 父容器不定宽高,因此子容器需要写为绝对定位,位置通过 left, top, bottom, right 来实现;
  • 两个子元素设置宽度为100%;
  • 受限制一方的高度,通过js计算得出,然后写在标签的 style 属性的 height 值;
  • 另一方的高度,其不设置 height 的值,而是通过设置 bottom 的值等于 style 属性的 height 值;
  • 此时,两个元素的位置和高度,都已经被设置完毕了;

5.2、左边栏上下区域划分

参考【套路5、当高度/宽度为动态获取,且相邻联动的两个区域】的方法二的思想。

HTML代码很简单,整体来看是两层DOM结构,代码如下:

<div className="aside">
    <div className='aside-nav'
         style={navStyle}>

    </div>

    <div className='aside-my-dear'
         style={my-dearStyle}>

    </div>
</div>
复制代码

上下两部分的样式也很简单,标准的位置固定但高度不固定(注意,下面代码,我没有写两个子元素的高度)

.aside-nav {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  // 测试用,查看显示效果是否正常
  //background-color: red;
}

.aside-dear {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  // 测试用,查看显示效果是否正常
  //background-color: green;
}
复制代码

现在,核心问题在于如何配置其高度,方法如下(见注释和上面的分析):

// 历史记录的数量,最小为1(为0时,该位置为占位符)
let dearLength = asideData.asideDear.length < 1 ? 1 : asideData.asideDear.length
        
// 最大只允许显示【10】行
const MAXDEARCOUNT = 10;
if (dearLength > MAXDEARCOUNT) {
    dearLength = MAXDEARCOUNT
}

// 历史记录的高度 =  dear的高度34px(24px字体+5*2px上下部分行间距)
//                  + 历史记录单行高度22px(12px字体高度+5*2上下行间距)
//                  + 最下方留白5px
const dearHeight = 34 + 22 * dearLength + 5;
const navStyle = {
    bottom: `${dearHeight}px`
}
const dearStyle = {
    height: `${dearHeight}px`
}
复制代码

此时我们已经配置好了两个子元素的高度,并且是动态生成的。

于是,给出 asideData.asideDear 的模拟数据:

asideDear: [
    {
        // 跳转url
        url: '#01',
        // 历史文字显示
        text: 'gfdsbfdsbdfsb'
    },
    {
        url: '#02',
        text: 'hrtnhr12'
    },
    {
        url: '#03',
        text: 'mythn13rfe'
    }
]
复制代码

此时,我们更改 asideDear 的元素个数,会发现 dear 区域的高度发生变化。

【缺点】

该方案是没问题的,硬要说缺点的话,就是上方区域按钮比较多的时候,默认出现滚动条比较丑。

解决方案也不难,提供几个参考:

  1. 考虑手写一个滚动条,样式和整体风格保持一致;
  2. 不显示滚动条,但提供拖拽功能(即类似触摸屏的操作方式),然后给是否达到最上、最小的提示;
  3. 隐藏滚动条,但依然可以使用滚动功能(即按钮和上分栏之间插入一层父级div,该级div比显示区域更宽,滚动条即位于这更宽的区域之内,但受到其父级div overflow:hidden的约束,无法显示);

我这里采用第三种解决方案,方法参考【套路6:无痕滚动】

套路6:无痕滚动

场景描述:

  • 假如父容器只有 100px 高,每个元素高 30px,可能有超过 3 个元素,但不允许超出父容器范围;
  • 因此一般情况下,设置父容器 overflow:auto; 显示滚动条;
  • 默认滚动条太难看,但不需要写自定义滚动条;
  • 因此需要支持鼠标滚动,但不显示鼠标滚动;

解决思路:

  1. 父容器和子元素之间加一层 div#box;
  2. 假设父容器和子元素的宽度为 100px, 默认滚动条宽度为 25px;
  3. div#box 的高度设为 100%(同父容器),宽度设为 125px(100+25)。假如元素过多,则 div#box 出现滚动条;
  4. 父容器设置 overflow:hidden,那么 div#box 出现滚动条的时候,将在父容器显示范围之外;
  5. 子元素设置 max-width:100px,确保无论是否有滚动条,子元素的显示效果都是一致的。

5.3、按钮的 CSS

这个太简单了,无非就是按钮里面一图标一文字,就只说一下思路吧。

  1. 按钮的大小是固定的(不固定也无所谓,肯定有参考位置),添加定位属性,比如相对定位 relative;
  2. 图标设置为绝对定位,上下居中(参考【套路2:定高不定宽,内有居中和相对最右侧的元素】),然后再设置相对左侧距离(left属性);
  3. 文字同样绝对定位,上下居中,通过 left 相对左侧的距离。唯一区别是,需要通过 right 属性设置最右侧的宽度,防止文字超出其应该显示的区域;

需要注意的是:

  • 这个样式按钮hover状态是 45px 高,因此认为一个按钮的高度为45px;
  • 但通过PS量实际两个按钮实际间距是50px(醉了!+1),无视,认为是原型图的问题,认为按钮与按钮的实际间距是45px(即按钮是连续的,不存在额外的间距);
  • 但又发现第一个按钮的高度是65px(醉了+2,这种设计很奇怪);
  • 推定按钮的父容器有额外 padding-top: 20px;

代码参照:

src/components/aside/index.jsx
src/components/aside/style.less
复制代码

5.4、历史记录

解决了高度问题后,这个就更简单了。

  • Dear 文字所在 div 宽度 100%,
  • 每个历史记录 div 宽度 100%,高度、行高等于实际高度;
  • 父级 div 设置 padding-bottom 下方留出应有的空隙;

完事。

5.5、总结

此时效果图如下:(假设按钮比较多的极端情况效果图)