We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
演示Demo地址(手机端打开):https://closertb.site/Klotski/index.html#demo 演示Demo源码:https://github.com/closertb/klotski
最近做了一个需求,准确说是迭代需求:加了一个头部概览(类似下图),以更好的让用户观察到营销变化,故事的开头就这样悄悄的埋下了伏笔。
以前这个页面只是一个评价列表(可上拉加载),为了数据更易读,列表的头采用了固定布局。然而加了这个概览时,产品没提,我就简单粗暴的将这个列表头换成了相对布局,ok,提测。 但第二天,我发现上拉加载数据多了,列表头部被顶上去之后,想再做筛选,就要再把列表上滑才能看到,这个体验非常之差。于是同事就说要不问问产品,要不把概览加概览做成固定。
我第一反应就是,恐怕提了之后,产品会让我只把筛选列表头部做成固定,注意那个只。
只
然后就有了下面的对话:
果然怕什么,来什么。但就像同事说的,自己问的需求,含着泪也要接下。
后面经评论提点,又加入了sticky的方案,确实是最优解。
以下代码是页面的dom结构
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>
因为页面本身就有scroll事件监听,所以第一个念头是用JS完成,但当时已经下班,又是周五,感觉5分钟内搞不定,所以我就跑了。
现在来尝试用JS实现,先理一下思路:
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; } }); }, []);
看起也不难,但其实离代码上线,还有很大优化的空间,后面会分析补充。
JS 看似很简单,但就像那句热门句子:这突如其来的噩耗,让本不富裕的家庭雪上加霜。在这种有下拉加载的页面,我们本来就在监听里面做了很多逻辑处理,所以能用CSS实现的,就尽量不要再去麻烦JS了。
这突如其来的噩耗,让本不富裕的家庭雪上加霜
首先理一下思路,深挖产品的需求:
当理清上面思路时,我们发现,其实就是当列表很长时,隐藏概览头部,简单用伪代码表示就是(vh是视口单位 ,100vh代表整个屏幕可视高度):
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中的定位(position)属性值中有个不常用的:sticky;
position
sticky
MDN官方文档摘录: 元素根据正常文档流进行定位,然后相对它的最近滚动祖先和最近块级祖先,包括table-related元素,基于top, right, bottom, 和 left的值进行偏移。粘性定位可以被认为是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。
这里我们在沿用JS的方案上进行更改,只需要将filter-bar 的定位属性改为粘性定位,就可以去除对 JS 的依赖;
.demo { max-height: 100%; overflow: scroll; .filter-bar { position: sticky; top: 0; } }
demo类作用于最外层dom(<div className={style.demo}>)上,其视高为100vh,当内容超出高度时为滚动;filter-bar 元素采用粘性定位,当高度距离demo元素大于0时,其采用相对定位,即以正常文档流的形式定位;当高度小于等于0时,其采用固定定位,就达到吸顶的效果。
<div className={style.demo}>
是不是感觉CSS很简单,稍微设置一下即搞定,只是要想到内容高度正好是100vh需要一点经(yun)验(qi),经常写H5的,sticky的方案相信也是新手黏来。其实不光简便,对比JS至少还有两个个优点:
100vh
filterBar
当然,viewport方案还有个ios手机的兼容性问题,由于safari的头部和底部滑动时可见性会改变,所以当Bar可见时,实际的100vh高于屏幕可见高度,就会导致吸顶头部被遮挡。到目前为止,虽然网上有很多说height: -webkit-fill-available;,但针对这种场景是无效的;但只要依赖100vh,都面临这种困局,safari太奇葩,下一个IE就是它了.
height: -webkit-fill-available;
经过上面分析,100vh在IOS safari上的致命问题,会让这种100vh这种纯CSS的方案褪色。但PC页面,或者你和我一样,要编写的页面是运行在APP中(即没有bar存在),那这种方案就是可行的。所有的方案都要具体场景,具体分析,没有谁出生就是完美。这里只是提高一种思考方式,长点见识。
而sticky方案不依赖于100vh,其可以用100%的写法,所以没有这个担忧,所以相比之下,最优解就是sticky; 但height: 100% 是个无底洞,你需要从html 标签写起,一直写到具有滚动的容器元素。
height: 100%
如果对重绘重排有兴趣,建议观看Chrome的官方博文: 浏览器四部曲
说完局部弹性吸顶,再说一个常见的,选择性吸底:在页面内容不足100vh时,我们希望Footer是吸底的,当页面内容大于100vh时,Footer处于正常文档流,让内容可视区域更大,而又不会因为内容太少影响美观,见图:
Footer
像第一张图那样不做定位的还是大有人在,因为他们坚信自己网站的内容不会出现不够的时候,但以前更常见做法是底部固定定位。
弹性吸底利用min-height 加绝对定位,其实现很简单。核心代码不超过5行css:
min-height
body{ position: relative; min-height: 100vh; } footer { width: 100%; position: absolute; bottom: 0; }
原理就是内容区域最低高度为一个屏幕,然后底部相对屏幕进行绝对定位;当内容变多时,高度大于100vh,由于是依赖bottom: 0;,所以会一直吸底,其巧妙之处就在于此。
bottom: 0;
针对于这个场景,height: -webkit-fill-available 就是有效的。
height: -webkit-fill-available
更多关于-webkit-fill-available, 参见[https://allthingssmitty.com/2020/05/11/css-fix-for-100vh-in-mobile-webkit/];
vh 确实是个好东西,可以解决移动端的适配问题, ;sticky 这种非热门属性(经常写PC的人)值得花时间去学习。我个人觉得作为一个合格的前端,CSS 仍然是必备技能,偶尔看看张旭鑫,还是有必要的;不要对JS产生太多的依赖,不是不可以,而是好钢要用在刀刃上。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
演示Demo地址(手机端打开):https://closertb.site/Klotski/index.html#demo
演示Demo源码:https://github.com/closertb/klotski
哪些想啥提啥的产品们
最近做了一个需求,准确说是迭代需求:加了一个头部概览(类似下图),以更好的让用户观察到营销变化,故事的开头就这样悄悄的埋下了伏笔。
以前这个页面只是一个评价列表(可上拉加载),为了数据更易读,列表的头采用了固定布局。然而加了这个概览时,产品没提,我就简单粗暴的将这个列表头换成了相对布局,ok,提测。
但第二天,我发现上拉加载数据多了,列表头部被顶上去之后,想再做筛选,就要再把列表上滑才能看到,这个体验非常之差。于是同事就说要不问问产品,要不把概览加概览做成固定。
我第一反应就是,恐怕提了之后,产品会让我
只
把筛选列表头部做成固定,注意那个只。然后就有了下面的对话:
果然怕什么,来什么。但就像同事说的,自己问的需求,含着泪也要接下。
后面经评论提点,又加入了sticky的方案,确实是最优解。
局部吸顶
以下代码是页面的
dom
结构JS 实现
因为页面本身就有scroll事件监听,所以第一个念头是用JS完成,但当时已经下班,又是周五,感觉5分钟内搞不定,所以我就跑了。
现在来尝试用JS实现,先理一下思路:
看起也不难,但其实离代码上线,还有很大优化的空间,后面会分析补充。
CSS Viewport实现
JS 看似很简单,但就像那句热门句子:
这突如其来的噩耗,让本不富裕的家庭雪上加霜
。在这种有下拉加载的页面,我们本来就在监听里面做了很多逻辑处理,所以能用CSS实现的,就尽量不要再去麻烦JS了。首先理一下思路,深挖产品的需求:
当理清上面思路时,我们发现,其实就是当列表很长时,隐藏概览头部,简单用伪代码表示就是(vh是视口单位 ,100vh代表整个屏幕可视高度):
那又怎样实现概览头部隐藏,而筛选头和列表又正好出于视口呢?
当用户往上划,只需要内容(筛选头和列表)正好是一个视口高度(100vh)时,概览头就恰好被隐藏,而筛选头又正好吸顶,用CSS实现就是类似这样的:
最优实现 CSS Sticky
在css中的定位(
position
)属性值中有个不常用的:sticky
;MDN官方文档摘录:
元素根据正常文档流进行定位,然后相对它的最近滚动祖先和最近块级祖先,包括table-related元素,基于top, right, bottom, 和 left的值进行偏移。粘性定位可以被认为是相对定位和固定定位的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。
这里我们在沿用JS的方案上进行更改,只需要将filter-bar 的定位属性改为粘性定位,就可以去除对 JS 的依赖;
demo类作用于最外层dom(
<div className={style.demo}>
)上,其视高为100vh,当内容超出高度时为滚动;filter-bar 元素采用粘性定位,当高度距离demo元素大于0时,其采用相对定位,即以正常文档流的形式定位;当高度小于等于0时,其采用固定定位,就达到吸顶的效果。对比
是不是感觉CSS很简单,稍微设置一下即搞定,只是要想到内容高度正好是
100vh
需要一点经(yun)验(qi),经常写H5的,sticky的方案相信也是新手黏来。其实不光简便,对比JS至少还有两个个优点:filterBar
高度,来填补筛选头离开正常文档流;(解决方案就是在筛选头外多套一层dom,并给一个固定高度,这样筛选头脱离正常文档流,但高度依旧还在);当然,viewport方案还有个ios手机的兼容性问题,由于safari的头部和底部滑动时可见性会改变,所以当Bar可见时,实际的100vh高于屏幕可见高度,就会导致吸顶头部被遮挡。到目前为止,虽然网上有很多说
height: -webkit-fill-available;
,但针对这种场景是无效的;但只要依赖100vh,都面临这种困局,safari太奇葩,下一个IE就是它了.经过上面分析,100vh在IOS safari上的致命问题,会让这种100vh这种纯CSS的方案褪色。但PC页面,或者你和我一样,要编写的页面是运行在APP中(即没有bar存在),那这种方案就是可行的。所有的方案都要具体场景,具体分析,没有谁出生就是完美。这里只是提高一种思考方式,长点见识。
而sticky方案不依赖于100vh,其可以用100%的写法,所以没有这个担忧,所以相比之下,最优解就是
sticky
; 但height: 100%
是个无底洞,你需要从html 标签写起,一直写到具有滚动的容器元素。如果对重绘重排有兴趣,建议观看Chrome的官方博文: 浏览器四部曲
弹性吸底
说完局部弹性吸顶,再说一个常见的,选择性吸底:在页面内容不足100vh时,我们希望
Footer
是吸底的,当页面内容大于100vh时,Footer
处于正常文档流,让内容可视区域更大,而又不会因为内容太少影响美观,见图:像第一张图那样不做定位的还是大有人在,因为他们坚信自己网站的内容不会出现不够的时候,但以前更常见做法是底部固定定位。
弹性吸底利用
min-height
加绝对定位,其实现很简单。核心代码不超过5行css:原理就是内容区域最低高度为一个屏幕,然后底部相对屏幕进行绝对定位;当内容变多时,高度大于100vh,由于是依赖
bottom: 0;
,所以会一直吸底,其巧妙之处就在于此。针对于这个场景,
height: -webkit-fill-available
就是有效的。更多关于-webkit-fill-available, 参见[https://allthingssmitty.com/2020/05/11/css-fix-for-100vh-in-mobile-webkit/];
总结
vh 确实是个好东西,可以解决移动端的适配问题, ;sticky 这种非热门属性(经常写PC的人)值得花时间去学习。我个人觉得作为一个合格的前端,CSS 仍然是必备技能,偶尔看看张旭鑫,还是有必要的;不要对JS产生太多的依赖,不是不可以,而是好钢要用在刀刃上。
The text was updated successfully, but these errors were encountered: