17
本文首发于政采云前端团队博客:基于 Vue 的两层吸顶踩坑总结

前言

近日,在做活动页的过程中遇到两层吸顶的需求,并且要兼容 IE9 及以上的浏览器。乍一看不就是个吸顶嘛,应该不难吧,事实证明还是踩了很多坑才出来。兼容性问题多到吐血,我太难了。废话不多说,先看一下两层吸顶的最终实现效果,如下图所示。

图片.gif

功能点:两层吸顶,因为 Tabs 区域比较长所以在滚动过程中点击一层 Tabs 会回弹至一层吸顶刚吸顶的位置,这个功能点和锚点有些类似。二层 Tabs 通过 hover 切换,没有回弹效果。

实现方式

本文主要通过 VueSticky 插件来实现吸顶,实现步骤描述如下:

  • 安装:npm install vue-sticky --save
  • 引入: import VueSticky from "vue-sticky"
  • 使用:
directives: {
    'sticky': VueSticky,
},
<ELEMENT v-sticky="{ zIndex: NUMBER, stickyTop: NUMBER, disabled: [true|false]}">
  <div> <!-- sticky wrapper, IMPORTANT -->
    CONTENT
  </div>
</ELEMENT>

看了 VueSticky 的源码后将该插件的实现原理简要概括如下:

首先判断该浏览器是否支持 position:sticky;,若支持就用 position:sticky; 来实现,若不支持就用 position:fixed; 的方式实现

所以大家不用担心兼容性问题,因为我已经帮大家测试过了,IE9 及以上的浏览器都可以支持。

生效条件

需要注意的是,使用 v-sticky 有几个必要条件,否则会失效:

  • 父元素不能设置 overflow:hidden 或者 overflow:auto 属性
  • 至少指定 top 、bottom 、left 、right 4 个值中的一个,否则只会处于相对定位
  • 父元素的高度不能低于 sticky 元素的高度
  • sticky 元素仅在其父元素内生效

问题汇总

◎ 吸顶“叠罗汉”

吸顶元素在滚动到组件底部时,在谷歌、火狐等浏览器中,两层吸顶在消失过程中有重叠现象,具体现象如下图所示:

0FABCFA5-B3D1-49C1-90C7-DCDDD458223E.png

主要原因:第一层吸顶还符合吸顶条件,第二层吸顶已经开始消失

解决方案:给第一层吸顶元素添加 minHeight 属性,其大小为第一层吸顶元素的高度与第二层吸顶元素的高度的和。这里有一个需要注意的点在于:一开始第一层吸顶元素的高度并非两者之和,所以这里就需要监听滚动事件,在吸顶元素距离底部的距离为两者高度之和的位置处给第一层吸顶元素添加 minHeight 属性

以下代码块中,sumHeight 表示两个吸顶元素的高度和,initialHeight 表示的是第一层吸顶元素的高度

  const offsetTop = document.querySelector(".xxx").offsetBottom;
  if (offsetBottom <= sumHeight) {
      document.querySelector(".xxx").style.minHeight = sumHeight;
  } else {
      document.querySelector(".xxx").style.minHeight = initialHeight;
  }

◎ 吸顶“舍不得离开”

在 IE 浏览器中,吸顶元素滚动到组件底部时不消失,具体现象如下图所示

702DD55C-D664-4a74-9DB1-BBC268992995.png

主要原因:在滚动过程中吸顶元素的 position:sticky; 属性始终存在

解决方案:监听滚动事件,当滚动到组件底部时,将 v-sticky="{ stickyTop: 0, disabled: false }" 中的 disabled 的值设为 true 即可

◎ 吸顶“难舍难分”

在 IE 浏览器中,两层吸顶元素始终吸在一起

ECE1EF93-1887-495c-8F35-19552522A406.png

主要原因:第二层吸顶元素在不需要吸顶的区域,它的 position 值也为 sticky

解决方案:监听滚动事件,在不需要吸顶的区域设置它的 position 值为 static 即可

◎ 吸顶“变形”

同样 DOM 结构的吸顶元素,在 IE 浏览器中,吸顶会变形

查看 vue-sticky 的源码,发现 position:fixed; 是设置在要吸顶的元素的第一个子元素上

58BFFFDC-8353-4A35-87FC-C7848155842E.png

因此为了兼容IE需要多加一层 div 结构

<div v-sticky="{ stickyTop: 0, disabled: false }>
    <div> <!-- sticky wrapper, IMPORTANT -->
        content
    </div>
</div>

注意事项

  • 组件的监听与移除

    • mounted 回调中加入以下代码
    mounted() {
        // handleScroll 为页面滚动的监听回调
        window.addEventListener('scroll', this.handleScroll);
    },
    • 同时要在 destroy 回调中移除监听
    beforeDestroy() {
        removeEventListener("scroll", this.handleScroll);
    },

优化点

  • 用监听事件监听滚动时,吸顶消失的很突兀

    • 判断浏览器是否支持 sticky ,若支持用 position:sticky; 实现,否则用 position:fixed;
    let supportCSSSticky = document.querySelector(".xxx").style.position === "sticky";
    if (!supportCSSSticky) {
        // 不支持的情况下监听滚动
    }
  • 图片懒加载

总结

本文简单的介绍了 VueSticky 插件的实现原理并分享了实战过程中出现的问题以及解决方案,希望对大家有所帮助。如果大家也遇到过两层吸顶的问题或者你还知道更好的解决方案,欢迎在评论区留下宝贵评论。

招贤纳士

政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 50 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、性能体验、云端应用、数据分析及可视化等方向进行技术探索和实战,推动并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。

如果你想改变一直被事折腾,希望开始能折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变既定的节奏,将会是“5 年工作时间 3 年工作经验”;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊… 如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 ZooTeam@cai-inc.com


政采云前端团队
3.8k 声望4k 粉丝

Z 是政采云拼音首字母,oo 是无穷的符号,结合 Zoo 有生物圈的含义。寄望我们的前端 ZooTeam 团队,不论是人才梯队,还是技术体系,都能各面兼备,成长为一个生态,卓越且持续卓越。