CSS 如何解决H5布局中的吸顶和吸底

目录
文章目录隐藏
  1. 局部吸顶
  2. 弹性吸底
  3. 总结

最近做了一个需求,准确说是迭代需求:加了一个头部概览(类似下图),以更好的让用户观察到营销变化,故事的开头就这样悄悄的埋下了伏笔。

以前这个页面只是一个评价列表(可上拉加载),为了数据更易读,列表的头采用了固定布局。然而加了这个概览时,产品没提,我就简单粗暴的将这个列表头换成了相对布局,ok,提测。

CSS 如何解决 H5 布局中的吸顶和吸底

但第二天,我发现上拉加载数据多了,列表头部被顶上去之后,想再做筛选,就要再把列表上滑才能看到,这个体验非常之差。于是同事就说要不问问产品,要不把概览加概览做成固定。

我第一反应就是,恐怕提了之后,产品会让我把筛选列表头部做成固定,注意那个”只”。

然后就有了下面的对话:

需求对话

果然怕什么,来什么,毕竟是很常规的操作。但就像同事说的,自己问的需求,含着泪也要接下。

局部吸顶

以下代码是页面的dom结构

<div id="demo" className={style.demo}>
  <h3 id="title" className="title">这是一个概览头部</h3>
  <div id="content" className="content">
    <div className="filter-bar">
      <h3>这是列表头部</h3>
      <h3>可筛选</h3>
      <h3>下面是滚动列表</h3>
    </div>
    <ul className="list">
      {arr.map(({ key, label }) => <li key={key}>{label}</li>)}
    </ul>
  </div>
</div>

JS 实现

因为页面本身就有 scroll 事件监听,所以第一个念头是用 JS 完成,但当时已经下班,又是周五,感觉 5 分钟内搞不定,所以我就跑了。

现在来尝试用 JS 实现,先理一下思路:

  • 监听页面的滚动,当 ul 元素顶部距离页面顶部大于 title 高度时,添加一个 css 类使筛选头部吸顶;
  • 当 ul 元素距离顶部小于等于 title 高度时,删除添加的类,取消筛选头部吸顶

JS 代码:

useEffect(() => {
  const demo = document.querySelector('#demo');
  const content = document.querySelector('#content');
  const titleHeight = document.querySelector('#title').clientHeight;
  let fixed = false;
  demo.addEventListener('scroll', (e) => {
    // 添加吸顶
    if (!fixed && e.target.scrollTop >= titleHeight) {
      fixed = true;
      content.classList.add('with-fixed');
    }
    // 取消吸顶
    if (fixed && e.target.scrollTop < titleHeight - 5) {
      content.classList.remove('with-fixed');
      fixed = false;
    }
  });
}, []);

看起也不难,但其实离代码上线,还有很大优化的空间,后面会分析补充。

CSS 实现

JS 看似很简单,但就像那句热门句子:这突如其来的噩耗,让本不富裕的家庭雪上加霜。在这种有下拉加载的页面,我们本来就在监听里面做了很多逻辑处理,所以能用 CSS 实现的,就尽量不要再去麻烦 JS 了。

首先理一下思路,深挖产品的需求:

  • 保持筛选头在可视范围之内(吸顶), 保证可筛选;
  • 当列表数据多时,尽可能多展示列表,即概览头部就没必要看到了;
  • 列表是上拉加载的;

当理清上面思路时,我们发现,其实就是当列表很长时,隐藏概览头部,简单用伪代码表示就是(vh 是视口单位 ,100vh 代表整个屏幕可视高度。如果你还没有掌握视口单位,请看教程:视口百分比长度 vh、vw、vi、vb、vmin、vmax 单位的了解):

if (titleHeight + filterBarHeight + listHeight > 100vh) {
  title.hide();
}

那又怎样实现概览头部隐藏,而筛选头和列表又正好出于视口呢?

filterBarHeight + listHeight = 100vh

当用户往上划,只需要内容(筛选头和列表)正好是一个视口高度(100vh)时,概览头就恰好被隐藏,而筛选头又正好吸顶,用 CSS 实现就是类似这样的:

// 不是完整代码,详情请看 demo:
.demo {
 :global {
   .title {
     height: 15vh;
     line-height: 15vh;
     text-align: center;
     border-bottom: 1PX solid #eee;
     background-color: #fff;
   }
   .filter-bar {
     height: 15vw;
     background-color: #888;
     display: flex;
     align-items: center;
   }
   .list {
     max-height: calc(100vh - 15vw);; // 这里的设置很重要
     overflow: scroll;
     background-color: rgba(127, 255, 212, .8);
   }

### 对比

是不是感觉 CSS 很简单,稍微设置一下即搞定,只是要想到内容高度正好是100vh需要一点经(yun)验(qi)。其实不光简便,对比 JS 至少还有三个优点:

  • JS 如果只是上面那样,直接将筛选头的定位改成固定定位,眼力好的人,其实是能感觉到列表有跳变的一瞬间,就是列表会突然上移filterBar高度,来填补筛选头离开正常文档流;(解决方案就是在筛选头外多套一层 dom,并给一个固定高度,这样筛选头脱离正常文档流,但高度依旧还在);
  • 当用户下拉加载很多数据时,还想看到概览头部,他就得费劲的往上划,直到移除吸顶筛选头的类;而 CSS 实现就不存在,他想看到对比数据时,只需要按住筛选头,往下一拉,就直接看到筛选头了(可以在筛选头部增加 js 来实现);
  • 当用 JS 来操作 Dom 元素重排时,这每年面试官说的那些重绘重拍我就不多说了,这消耗的性能肯定高于 CSS 实现;

当然缺点也是存在的:

  • 兼容性问题,倒不是说 vh 的兼容性,而是在 ios 手机上,这方案有 bug。由于 safari 的头部和底部滑动时可见性会改变,所以当 Bar 可见时,实际的 100vh 高于屏幕可见高度,就会导致吸顶头部被遮挡。到目前为止,虽然网上有很多说height: -webkit-fill-available;,但针对这种场景是无效的;

弹性吸顶

经过上面分析,100vh 在 IOS safari 上的致命问题,会让这种纯 CSS 的方案褪色。但 PC 页面,或者你和我一样,要编写的页面是运行在 APP 中(即没有 bar 存在),那这种方案就是可行的。所有的方案都要具体场景,具体分析,没有谁出生就是完美。

如果对重绘重排有兴趣,建议观看Chrome 的官方博文: 浏览器四部曲

弹性吸底

说完局部弹性吸顶,再说一个常见的,选择性吸底:在页面内容不足 100vh 时,我们希望Footer是吸底的,当页面内容大于 100vh 时,Footer处于正常文档流,让内容可视区域更大,而又不会因为内容太少影响美观,见图:

弹性吸底

像第一张图那样不做定位的还是大有人在,因为他们坚信自己网站的内容不会出现不够的时候,但以前更常见做法是底部固定定位。

弹性吸底利用min-height 加绝对定位,其实现很简单。核心代码不超过 5 行 css:

body{
  position: relative;
  min-height: 100vh;
}

footer {
  width: 100%;
  position: absolute;
  bottom: 0;
}

原理就是内容区域最低高度为一个屏幕,然后底部相对屏幕进行绝对定位;当内容变多时,高度大于 100vh,由于是依赖bottom: 0;,所以会一直吸底,其巧妙之处就在于此。

针对于这个场景,height: -webkit-fill-available 就是有效的。

height: -webkit-fill-available 适配属性

更多关于-webkit-fill-available, 参见[allthingssmitty];

总结

vh 确实是个好东西,可以解决移动端的适配问题。我个人觉得作为一个合格的前端,CSS 仍然是必备技能,不要对 JS 产生太多的依赖,不是不可以,而是好钢要用在刀刃上。

演示 Demo 地址(手机端打开

演示 Demo 源码 点击这里

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » CSS 如何解决H5布局中的吸顶和吸底

发表回复