前端重构范式之 position
source link: http://scala.cool/2018/09/febible-position-layout/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
本文旨在让你更深入地了解 position
,并为你提供一套使用 position
的范式,为你使用 position
提供一点建议和参考。
在此之前先让我们来看看 learncss 中文文档中对 position
的定义
以上是较为官方的定义,你可以从中获取到关于 position
的一些基本操作,接下来让我们试图从另一个角度来理解 position
.
position
是一种描述物体相对位置的艺术,它的核心是「参考坐标系」的选择
正如我们在现实生活中看到的那样,我们需要使用各种不同的「参考坐标系」,来更方便准确地描述我们身边各种物体的相对位置。因为,我们已经知道,如果单纯依靠一个坐标系来描述这个世界上的各个位置,这个世界将变得异常复杂。我们需要使用不同的坐标系,来帮住我们简化对位置的描述。
在我们的网页布局中也是一样,我们不能只采用一个坐标系来对网页中的元素进行定位,那将使得我们的网页变得异常复杂且不易维护。我们需要多个不同的坐标系,来帮助我们对网页进行更好地布局。css中的 position
,就是我们俗称的「定位」,就是定义了这么一套规则,使得我们可以应用不同的坐标系,来对页面中的元素进行更好地布局和管理。
反应在具体的规则当中,就是我们熟知的 static
, relative
, absolute
, fixed
。而它们最本质的区别就在于「参考坐标系」的不同。其他的一系列问题,例如典型的元素层级,都可以归结为 position
所带来的副作用。
到这里,你或许对 position
有了一个不一样的认识,接下来可以尝试着思考以下几个问题
position position
不知道?没关系,或许以下的文章能够给你一点提示
副作用
首先我们需要讨论的是 position
的副作用,原因很简单,你手中有一个锤子,你总得知道这个锤子能干什么,不能干什么。我们只有像如来佛祖了解悟空那样了解 position
,才能发挥出悟空( position
)的真正威力!
要分析 position
的副作用,我们或许可以采用这样的思路:
以下的组件均指开启了 position
定位的元素,这里把它称为组件
- 把该组件以及内部的元素作为一个整体,分析 它对外界 或者 外界对它 有什么影响。简称对外的影响
- 单独分析该组件,不考虑对外的影响,分析该
position
属性对元素内部的元素会带来什么影响。简称对内的影响 - 除了单独分析每个定位属性的副作用,我们还需要考虑不同的定位属性之间的 组合 会带来什么样的副作用,例如最为典型的
relative
和absolute
顺着这个思路,接下来我们来思考不同的 position
属性都会带来什么影响或副作用
副作用 absolute
和 relative
relative
和 absolute
是我们再熟悉不过的一对欢喜冤家,它们两个经常是成对出现,产生的副作用也更多地来自于它们之间的组合。因此在这里将它们放在一起分析。
特性与副作用往往是相伴而生的。我们先来回顾一下,这两个属性各有什么特性?
position:absolute
元素具备的特性如下:
inline-block
鉴于本人的经验限制,下面的副作用例子展示的也只是其中的一部分,如有更好的补充,欢迎在下方评论区中与大家一起分享
position:relative
元素具备的特性如下:
relative z-index
深度 relative
如果我们单独分析 absolute
的副作用,我们会把它简单地归结为脱离文档流,从而导致两个最为明显的副作用:改变布局和覆盖。
但是我认为这属于它本身具备的特性,并不是它的副作用。我觉得在现实应用中真正存在的副作用在于 relative
和 absolute
的共同应用所造成的问题,在这里我把它称作「深度 relative
」。
所谓的「深度 relative
」,指的是 relative
包裹的组件处于较深的层级,也就是被包裹的比较深,导致其中的 absolute
组件要想不被其他元素覆盖,需要一级一级地往f父元素设置 z-index
或者排查 overflow
属性。这个问题最关键的核心就是 relative
组件层级太深。
这里讲得可能有点抽象,让我们一起来看一个例子:
这是我在实际项目开发中遇到的一个典型的例子。简单来讲,就是点击重选按钮,在指定的位置跳出图标选择框,并且在窗口滚动时依然有效。具体效果如下图所示:
就是这么一个简单的例子,当然以上是已经实现好的效果。如果你没有遇到过类似的问题,你会觉得这个问题很简单,使用 relative
和 absolute
就能轻松解决。但是,在这里先卖个关子,以上的效果是使用 fixed
实现的。
我们首先使用我们典型的 relative
和 absolute
来实现以上的效果,我们可以得到如下的效果:
首先你会发现一个最为明显的问题,就是图标选择框被其他元素盖住了,这个时候的原因就很多样了,或许是因为它所在的祖先元素的元素层级没有人家高,又或许是因为祖先元素设置了 overflow:hidden
等等。这个时候如果我们依然要坚持使用 relative
和 absolute
的解决方案,那我们通常会采取的解决方案就是,一级一级的网上排查 overflow:hidden
又或者是设置 z-index
,直到解决问题为止。
z-index
说到 z-index
,这里可以简单下使用方法。它仅在 positon
属性不为 static
时起作用。从 position
从基础可以了解到 absolute
和 fixed
会让元素脱离文档流,这两个修饰的元素默认层级自然比文档流的要高,然而 relative
则不会脱离文档流,但当它设置了 TRBL 时它也会覆盖文档流,其默认层级自然也是比后者要高的, 脱离文档流和层级的概念不能混淆 。
同级原则
所谓同级,顾名思义,也就是同级间层级的比较,一个元素 position
不为 static
后,如果不给它的 z-index
设定值,默认为0,由下图可以看到,A 元素设定了 z-index
为0,B元素未设定,E 元素设定了 z-index
为负数,按照顺序 B 覆盖住了 A,而E则被A盖住。往后则是 z-index
大的把小的覆盖。
在此仅展示结构,css 代码则省去。
<divid="1"style="position: absolute;z-index: 0">A</div> <divid="2"style="position: absolute">B</div> <divid="3"style="position: absolute;z-index: 1">C</div> <divid="4"style="position: absolute;z-index: 2">D</div> <divid="5"style="position: absolute;z-index: -1">E</div>
多级原则
看到这个标题,你可能会问,这是啥意思?其实很简单,它的意思是,要比较的元素不再是简单的同级概念,而是其中一个或两者都有祖先元素。看看下面这个代码:
<divid="1"style="position:relative;z-index:2;"> <divid="1-1"style="position:relative;z-index:1;">A</div> </div> <divid="2"style="position:relative;z-index:1;"> <divid="2-1"style="position:relative;z-index:999;">B</div> </div>
效果图:
可见,A、B父元素的层级影响了相应的子元素的层级,就算 B 的 z-index
设的再大,它的父元素的 z-index
总是小于 A 的父元素的 z-index
值,这时候不论 A 的 z-index
怎么变化,元素 A 就会像图中一样一直压着 B,这就是从父原则。不过这里要划一下重点,这里的父元素不一定要同级,换句话说,两个元素间的层级比较,是 相应的的同级祖先元素各自往下找到第一个 position
不为 static
的的两个元素之间的比较 。举个栗子,结构改成下面这个结构,实现效果是一样的。
<divid="1"style="position:relative;z-index:2;"> <divid="1-1"style="position:relative;z-index:1;">A</div> </div> <div> <divid="2"style="position:relative;z-index:1;"> <divid="2-1"style="position:relative;z-index:999;">B</div> </div> </div>
以上讲了这么多,你或许觉得怎么这么繁琐,要考虑这儿考虑那儿的,也会发现即使你用这种方法达到了你想要的效果,它也并不能从根本上解决问题。我一个简单的小组件,你却需要我去影响那么多父级元素,谁知道会造成什么意想之外的情况。除了这个,设置过多的 z-index
会导致页面层级管理的混乱,使得页面很难管理,所有有一个原则,要么对 z-index
做明文的规定,要么就少用层级过深的 z-index
。
我这里有一种较好的解决方案,就是「 fixed
组件」。如果你有更好的解决方案,欢迎在下方评论区中进行分享。
fixed组件
我们都知道当元素设置了 position:fixed
之后,该元素将具备以下的特性:
- 永远相对于浏览器窗口进行定位
- 固定在浏览器窗口的某个位置,不随滚动条滚动
- 脱离文档流
以上是我们众所周知的一些特性,其实根据以上的特性,我们还可以推出以下这些特性:
- 单独使用
fixed
属性,不需要再开启父元素的定位,使得,减少了很多的z-index属性的设置
在下面的例子中,我们只对「图标组件」设置了 position:fixed
定位,页面中的其他大部分都是 position:static
组件,因此可以很轻松的解决原来的覆盖问题,也不会影响其他布局,更不会造成 z-index
管理混乱的问题。
- 使用js控制
fixed
组件的margin
,我们可以实现,fixed组件
相对于父元素进行定位
通常我们在应用 fixed
组件的时候,都要解决 fixed
组件的滚动问题。通常 fixed
组件不会随着滚动条的滚动而滚动,但是我们拥有强大的js,我们可以通过为 fixed
组件设置一个固定的 margin-top
,然后通过js动态的控制这个值,使得它可以相对父元素进行滚动。因为我们都知道, margin
属性调节的是 box
之间的距离, fixed
组件脱了文档流,但它依然是一个box,因此我们可以使用margin来调节 fixed
组件与父元素之间的相对位置。
就拿上面的例子而言,具体代码如下所示:
副作用 fixed
fixed 失效
在固定定位元素的父元素上应用transform属性,固定定位的元素会相对于父元素来定位
具体可参考以下这篇博文,简单来说就是 transform
属性会对 fixed
组件造成影响
Eric’s Archived Thoughts: Un-fixing Fixed Elements with CSS Transforms
范式
讲了这个多关于 position
的副作用,我想到这里你或许也早已经有了一套自己的范式,其实里面的很多细节我们都已经在前面提到过。在这里我将分享一套属于我自己的 position
范式,学才疏浅,请不要见怪
- 相对于具体元素的小组件可以采用
relative
+absolute
或者「fixed
组件」的解决方案。如果组件层级较深,推荐使用「fixed
组件」的方式来进行定位;具体可以参照上面提到过的图标选择框的例子。 - 相对于浏览器窗口固定的小组件,推荐是用
fixed
定位,例如右下角的小弹窗,固定的底部导航 - 要尽量减少
z-index
的嵌套使用 - 在使用
fixed
定位时,要留意transform
属性对它的影响
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK