用户体验思考与flex三坑:元素不均分溢出不省略和垂直不滚动

Posted 恪愚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用户体验思考与flex三坑:元素不均分溢出不省略和垂直不滚动相关的知识,希望对你有一定的参考价值。

flex已经越来越成为前端不可避免的话题。曾经为了搞清flex的原理偶然画了一张图。但后来发现只是冰山一角。

在某些你想实现的交互效果中使用flex后可能会发现并不起作用。通过我的实践,大致有三个问题:子元素不平分父元素空间、对文字设置了溢出省略却失效、flex下列表溢出但是滚动失效。常常会让人摸不着头脑。这里记录探究。

元素不均分

“均分列”,也就是子元素等宽应该是项目中最常见的效果。我常常在项目中见到这样的代码:

flex: 1;

似乎有很多人认为在 Flex 布局中这样显式设置即可“一劳永逸”。
但事实是什么?事实是我们都知道flex: 1是下面这三个属性的简写:

flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;

如果未显式设置flex时,其初始值是:

flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;

flex-basis 为 auto 时,Flex布局元素的宽度是max-content。也就是说,flex:1时,flex-basis变成了0%,这将覆盖Flex布局的内在尺寸max-content。但Flex布局的基本尺寸变为 0 以后,由于 flex-grow的存在,它们都可以增长以填补父元素的剩余空间。
而实际上,在这种情况下,flex-shrink不再做任何事情,因为所有的Flex布局现在的宽度都是0,并且正在自动填补可用空间。只不过,这时候Flexbox容器却并不一定有剩余空间了,甚至有可能空间不足。这时候flex:1也就不能均分Flexbox容器了。
这是有情况可循的:

可以看到,在空间拥挤的情况下,后面的元素明显要比前面的元素宽。而且越来越宽。就好像前面的元素因为不知道要“压缩”多少所以按照内容压缩到能压缩的最小宽度,但是到后面元素时发现空间够了不用收缩了一样。
其实这就是触发了flex的边缘情况。说到这里我们要简单说一下max-content:容器占据的它所能占据宽度的全部就是max-content的尺寸。也有人叫“假设宽度”(因为你可能并不知道到底是多宽)。比如:

有意思的是,相对应的min-content是在内部做了一次比较:采用内部元素最小宽度值最大的那个元素的宽度作为最终容器的宽度。

回到正题,上面其实就是触发了flex的边缘情况。在W3C中有一段描述翻译过来是这样的:“默认情况下,弹性Flex(设置为flex:1的Flex元素)在收缩的时候,其宽度不会小于其最小内容尺寸(即 min-width,也就是max-content或固定尺寸元素的长度)。要改变这一点,需要显式设置min-widthmin-height的值”。
括号里的话非常重要!它会涉及min-width的值。默认情况下,min-width的值为 auto,它会被计算为 0。当一个元素采用了Flex布局时,min-width的值又不会被计算为 0,它的值为max-content。这时就会触发边缘计算。

为此,要真正达到均分列,只显式设置flex:1还不行,还需要在Flex项目上显式设置min-width的值为0:

flex: 1;
min-width: 0;

溢出不省略

你应该遇到过这样的问题吧?就像我之前在项目中遇到的:

在笔者的这篇文章中,我曾经给出了一个比较复杂的解读。但是后来看到了W3C中的这段话:“主轴上Flex元素的overflow属性是visible时,主轴上Flex元素的最小尺寸(min-size)将会指定一个自动的最小尺寸”。
结合上面那段W3C的语录,很容易想到问题出在了min-width属性上!
所以解决方法就是在需要换行/省略的元素上,将min-width显式地设为0。一般这样操作:

flex: 1 1 0%;
min-width: 0;

滚动失效

之前在我司加价换购的项目中,我惊讶的发现,列表中明明有超过显示区域的元素数量,但是上下并不滚动。一开始是以为动态高度导致了没有识别到元素。但是发现此时父元素上已经有了overflow-y: scroll; 这说明不是这里的问题。最终在我无意间注掉了display: flex;发现可以了…

这个bug我问了我司的其他同事并没有遇到过,说明和使用的结构有关:
因为是一个大弹层,所以我在最外层设置了flex,然后在主体区域使用了flex: 1让其能够自适应。在主体内还有一层,是滚动元素的父元素,接着才是真正的滚动内容。
html结构大概是这样的:

<div class="main-container">
	<!-- main-container是flex布局,里面上下两层 -->
	<div class="fixed-container">头部</div>
	<!-- 下面是主体区域,content-wrapper是最外层的 -->
	<div class="content-wrapper">
		<!-- 下面的overflow-container是滚动容器,里面的over-content才是滚动项 -->
		<div class="overflow-container">
			<div class="overflow-content"> 滚动项,不止一条 </div>
		</div>
	</div>
</div>

这同样是触发了flex边缘计算导致的。这个问题的解决方案就是给滚动容器的父元素,也就是设置了flex: 1的元素添加min-height属性:

.content-wrapper 
	/** ... */
	flex: 1;
	min-height: 0; /** !!!*/

.overflow-container 
	flex: 1;
	overflow-y: auto; /** 然后这个就能发挥作用了 */

后来阅读 @张旭鑫 张大的 flex-end为什么overflow无法滚动及解决方法 才知道,并且很快就遇到了另一个场景,同样会导致滚动失效。这里凑巧了一直想记录下。
这种情况是由于justify-content: flex-end;属性导致的,是因为:

在 Web 中,我们的书写习惯和阅读模式是从左到右(LTR),从上到下。也就是说,一般情况之下(先不考虑其他的书写模式),水平方向内容向右溢出,垂直方向的内容向底部溢出,即滚动条在设计的时候,就约定了,只有容器下方(或右侧)内容有多余,才需要滚动。

在滚动容器(它刚好是Flexbox容器)显式设置了justify-content: flex-end(主轴方向从flex-start换成了flex-end)。这就导致,如果有内容是在上方或左侧超过容器的尺寸限制,滚动条是不会有任何变化的。也因此,滚动容器里面内容溢出容器的方向不是在容器的下方或者右侧,而是在容器的顶部和左侧,自然就无法触发滚动条的出现。

简单地说,flex-end 会让内容反向溢出,也就没有滚动条,自然无法滚动。要解决这个问题,只需要给滚动方向上的第一个子元素margin-*: auto;实现块元素的对齐效果,并且删掉flex-end即可。


瞎想

最近无事,到处看APP和网站,倒也真给我的用户体验进度出了一份力。有些地方感觉还是值得想一下的。
比如弹窗,这里先不说弹窗与用户打扰的问题。现在的app和移动端网站中对弹窗的设计逻辑可以分为两种:侧滑返回时弹窗消失(这一种是把弹窗作为一个大组件、一个拥有独立操作与逻辑的功能部分了)和侧滑返回时直接返回上一页面(这一种是把弹窗当做当前页面的附属小功能了,和页面一体)。这玩意你很难说谁对谁错的问题,用户更喜欢哪种也只能靠数据说话。甚至哪一种功能用哪一种弹窗设计都是可以规范一二的。

比如移动端网页的「返回」操作。之前看到 @HullQin(掘金) 说可以用push代替做侧滑返回逻辑。但笔者觉得这里还有两个问题:①侧滑返回这件事在移动端只有浏览器APP中可以,在QQ内置(默认打开)浏览器页面中侧滑就直接退出了,行为的不统一带来的割裂感很难被交互认同,很离谱②怎么判定用户是想回到网站首页或者是网站某个页面还是直接回到上一个打开的页面,这确实值得思考和改进。

这里没有“批判”的意思。事实上这位老哥对极致用户体验的研究挺深入的,能够给人不少感悟。

比如响应式。拿我司多件优惠和加价换购买家侧中的某一个场景(左边)来看,如果拿到更小的空间,这时候怎么更好地展示呢?

目前可以确定的是,下面那种方式 所代表的展示形式 似乎并不被设计师喜爱。各位有遇到类似的场景吗?

以上是关于用户体验思考与flex三坑:元素不均分溢出不省略和垂直不滚动的主要内容,如果未能解决你的问题,请参考以下文章

CSS文本溢出省略号在Grid / Flex中不起作用

文字溢出省略和用户体验优化

文字溢出省略和用户体验优化

box-flex不均分问题

flex自适应宽度显示省略号

flex 容器中的项目溢出 x 轴中的父元素