干货 | CSS 中重要的层叠概念

*本文作者为 SHERlocked93,转载已获得作者授权。

原文链接:

https://segmentfault.com/a/1190000016489078

最近在项目的过程中遇到了一个问题,menu-bar希望始终显示在最上面,而在之后的元素都显示在它之下,当时设置了 z-index 也没有效果,不知道什么原因,因此找了一下css有关层叠方面的资料,解决了这个问题,这里记录一下~

屏幕是一个二维平面,然而HTML元素却是排列在三维坐标系中,x为水平位置,y为垂直位置,z为屏幕由内向外方向的位置,我们在看屏幕的时候是沿着z轴方向从外向内的;由此,元素在用户视角就形成了层叠的关系,某个元素可能覆盖了其他元素也可能被其他元素覆盖;

那么这里有几个重要的概念:层叠上下文 (堆叠上下文, Stacking Context)、层叠等级 (层叠水平, Stacking Level)、层叠顺序(层叠次序, 堆叠顺序, Stacking Order)、z-index

声明:

  1. 以下定位元素指的是position: absolute|fixed|relative|sticky
  2. 以下非定位元素指的是position: initial|static
  3. 关于层叠上下文还有一个类似的概念:块级格式化上下文(BFC, Block Formatting Context),可以参考一下 CSS 中重要的BFC,其中还介绍了一些文档流的内容;
  4. 本文蛮长的,但是如果你有勇气看完,那应该对层叠有关概念就基本掌握了 (~o ̄▽ ̄)~
  1. 层叠上下文 (Stacking Context)

层叠上下文 (堆叠上下文, Stacking Context),是HTML中一个三维的概念。在CSS2.1规范中,每个元素的位置是三维的,当元素发生层叠,这时它可能覆盖了其他元素或者被其他元素覆盖;排在z轴越靠上的位置,距离屏幕观察者越近

文章<关于z-index 那些你不知道的事>有一个很好的比喻,这里引用一下;

可以想象一张桌子,上面有一堆物品,这张桌子就代表着一个层叠上下文。 如果在第一张桌子旁还有第二张桌子,那第二张桌子就代表着另一个层叠上下文。

现在想象在第一张桌子上有四个小方块,他们都直接放在桌子上。 在这四个小方块之上有一片玻璃,而在玻璃片上有一盘水果。 这些方块、玻璃片、水果盘,各自都代表着层叠上下文中一个不同的层叠层,而这个层叠上下文就是桌子。

每一个网页都有一个默认的层叠上下文。 这个层叠上下文(桌子)的根源就是<html></html>。 html标签中的一切都被置于这个默认的层叠上下文的一个层叠层上(物品放在桌子上)。

当你给一个定位元素赋予了除 auto 外的 z-index 值时,你就创建了一个新的层叠上下文,其中有着独立于页面上其他层叠上下文和层叠层的层叠层, 这就相当于你把另一张桌子带到了房间里。

层叠上下文1 (Stacking Context 1)是由文档根元素形成的, 层叠上下文2和3 (Stacking Context 2, 3) 都是层叠上下文1 (Stacking Context 1) 上的层叠层。 他们各自也都形成了新的层叠上下文,其中包含着新的层叠层。

在层叠上下文中,其子元素按照上面解释的规则进行层叠。形成层叠上下文的方法有:

  • 根元素<html></html>
  • position值为absolute | relative,且z-index值不为 auto
  • position 值为 fixed | sticky
  • z-index 值不为 auto 的flex元素,即:父元素display: flex | inline-flex
  • opacity 属性值小于 1 的元素
  • transform 属性值不为 none的元素
  • mix-blend-mode 属性值不为 normal 的元素
  • filterperspectiveclip-pathmaskmask-imagemask-bordermotion-path 值不为 none 的元素
  • perspective 值不为 none 的元素
  • isolation 属性被设置为 isolate 的元素
  • will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  • -webkit-overflow-scrolling 属性被设置 touch的元素

总结:

  1. 层叠上下文可以包含在其他层叠上下文中,并且一起组建了一个有层级的层叠上下文
  2. 每个层叠上下文完全独立于它的兄弟元素,当处理层叠时只考虑子元素,这里类似于BFC
  3. 每个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会在父级叠上下文中按顺序进行层叠

2. 层叠等级 (Stacking Level)

层叠等级 (层叠水平, Stacking Level) 决定了同一个层叠上下文中元素在z轴上的显示顺序的概念

  • 普通元素的层叠等级优先由其所在的层叠上下文决定
  • 层叠等级的比较只有在同一个层叠上下文元素中才有意义
  • 在同一个层叠上下文中,层叠等级描述定义的是该层叠上下文中的元素在Z轴上的上下顺序

注意,层叠等级并不一定由 z-index 决定,只有定位元素的层叠等级才由 z-index 决定,其他类型元素的层叠等级由层叠顺序、他们在HTML中出现的顺序、他们的父级以上元素的层叠等级一同决定,详细的规则见下面层叠顺序的介绍。

3. z-index

在 CSS 2.1 中, 所有的盒模型元素都处于三维坐标系中。 除了我们常用的横坐标和纵坐标, 盒模型元素还可以沿着”z 轴”层叠摆放, 当他们相互覆盖时, z 轴顺序就变得十分重要。

— CSS 2.1 Section 9.9.1 – Layered presentation

z-index 只适用于定位的元素,对非定位元素无效,它可以被设置为正整数、负整数、0、auto,如果一个定位元素没有设置 z-index,那么默认为auto;

元素的 z-index 值只在同一个层叠上下文中有意义。如果父级层叠上下文的层叠等级低于另一个层叠上下文的,那么它 z-index 设的再高也没用。所以如果你遇到 z-index 值设了很大,但是不起作用的话,就去看看它的父级层叠上下文是否被其他层叠上下文盖住了。

4. 层叠顺序 (Stacking Order)

层叠顺序 (层叠次序, 堆叠顺序, Stacking Order) 描述的是元素在同一个层叠上下文中的顺序规则,从层叠的底部开始,共有七种层叠顺序:

  1. 背景和边框:形成层叠上下文的元素的背景和边框。
  2. 负z-index值:层叠上下文内有着负z-index值的定位子元素,负的越大层叠等级越低;
  3. 块级盒:文档流中块级、非定位子元素;
  4. 浮动盒:非定位浮动元素;
  5. 行内盒:文档流中行内、非定位子元素;
  6. z-index: 0:z-index为0或auto的定位元素, 这些元素形成了新的层叠上下文;
  7. 正z-index值:z-index 为正的定位元素,正的越大层叠等级越高;

同一个层叠顺序的元素按照在HTML里出现的顺序层叠;第7级顺序的元素会显示在之前顺序元素的上方,也就是看起来覆盖了更低级的元素:

5. 实战

5.1 普通情况

三个relative定位的div块中各有absolute的不同颜色的span.redspan.greenspan.blue,它们都设置了position: absolute

参见Codepen – 普通情况

那么当没有元素包含z-index属性时,这个例子中的元素按照如下顺序层叠(从底到顶顺序):

  1. 根元素的背景和边界
  2. 块级非定位元素按HTML中的出现顺序层叠
  3. 行内非定位元素按HTML中的出现顺序层叠
  4. 定位元素按HTML中的出现顺序层叠

红绿蓝都属于 z-index 为auto的定位元素,因此按照7层层叠顺序规则来说同属于层叠顺序第6级,所以按HTML中的出现顺序层叠:红->绿->蓝

5.2 在相同层叠上下文的父元素内的情况

红绿位于一个div.first-box下,蓝位于div.second-box下,红绿蓝都设置了position: absolutefirst-boxsecond-box都设置了position: relative

参见Codepen – 父元素不同但都位于根元素下

这个例子中,红蓝绿元素的父元素first-boxsecond-box都没有生成新的层叠上下文,都属于根层叠上下文中的元素,且都是层叠顺序第6级,所以按HTML中的出现顺序层叠:红->绿->蓝

5.3 给子元素增加 z-index

红绿位于一个div.first-box下,蓝黄位于div.second-box下,红绿蓝都设置了position: absolute,如果这时给绿加一个属性z-index: 1,那么此时.green位于最上面;

如果再在.second-box下.green后加一个绝对定位的 span.gold,设置z-index: -1,那么它将位于红绿蓝的下面;

参见Codepen – 设置了z-index

这个例子中,红蓝绿黄元素的父元素中都没有生成新的层叠上下文,都属于根层叠上下文中的元素

  1. 红蓝都没有设置 z-index,同属于层叠顺序中的第6级,按HTML中的出现顺序层叠;
  2. 绿设置了正的 z-index,属于第7级;
  3. 黄设置了负的 z-index,属于第2级;

所以这个例子中的从底到高显示的顺序就是:黄->红->蓝->绿

5.4 在不同层叠上下文的父元素内的情况

红绿位于一个div.first-box下,蓝位于div.second-box下,红绿蓝都设置了position: absolute,如果first-box的z-index设置的比second-box的大,那么此时无论蓝的 z-index 设置的多大z-index: 999,蓝都位于红绿的下面;如果我们只更改红绿的z-index值,由于这两个元素都在父元素first-box产生的层叠上下文中,此时谁的z-index值大,谁在上面;

参见Codepen – 不同层叠上下文的父元素

这个例子中,红绿蓝都属于设置了z-index的定位元素,不过他们的父元素创建了新的层叠上下文;

  1. 红绿的父元素first-box是设置了正z-index的定位元素,因此创建了一个层叠上下文,属于层叠顺序中的第7级;
  2. 蓝的父元素second-box也同样创建了一个层叠上下文,属于层叠顺序中的第6级;
  3. 按照层叠顺序,first-box中所有元素都排在second-box上;
  4. 红绿都属于层叠上下文first-box中且设置了不同的正 z-index,都属于层叠顺序中第7级;
  5. 蓝属于层叠上下文second-box,且设置了一个很大的正 z-index,属于层叠元素中第7级;
  6. 虽然蓝的 z-index 很大,但是因为second-box的层叠等级比first-box小,因此位于红绿之下;

所以这个例子中从低到到显示的顺序:蓝->红->绿

(我遇到的的情况就属于这个例子类似情形)

5.5 给子元素设置 opacity

红绿位于div.first-box下,蓝位于div.second-box下,红绿蓝都设置了position: absolute,绿设置了z-index: 1,那么此时绿位于红蓝的最上面;

如果此时给first-box设置opacity: .99,这时无论红绿的 z-index 设置的多大z-index: 999,蓝都位于红绿的上面;

如果再在.second-box.green后加一个span.gold,设置z-index: -1,那么它将位于红绿蓝的下面;

参见Codepen – opacity的影响

之前已经介绍了,设置opacity也可以形成层叠上下文,因此:

  1. first-box设置了opacityfirst-box成为了一个新的层叠上下文;
  2. second-box没有形成新的层叠上下文,因此其中的元素都属于根层叠上下文;
  3. 黄属于层叠顺序中第2级,红绿属于第7级,first-box属于第6级,蓝属于层叠顺序中第6级且按HTML出现顺序位于first-box之上;

所以这个例子中从低到到显示的顺序:黄->红->绿->蓝

网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

参考:

  1. 你不知道的Z-Index
  2. MDN – z-index
  3. What No One Told You About Z-Index
  4. 彻底搞懂CSS层叠上下文、层叠等级、层叠顺序、z-index
  5. 前端性能优化之更平滑的动画
  6. 关于z-index 那些你不知道的事
  7. 聊聊CSS中的层叠相关概念

推介阅读:

  1. CSS 中重要的BFC

Web性能优化 | 如何提高首屏加载速度?

在 FrontJS 的使用反馈中,页面和资源的加载速度是很多用户密切关注的数据,想要提高加载速度,就需要不断的对网站进行优化。相信大家都有在浏览网页时遇到过页面加载不完全的问题。作为网站的“建造人”,我们不仅是开发者,同时也是用户。当我们在网页上看到各种红叉和长时处于 loading 状态的信息时,会瞬间失去浏览页面的兴致,变得不耐烦起来——尤其是首屏,加载的快与慢直接影响着用户的使用体验及滞留时间。

相对于移动端的首屏,Web端想让用户看到的信息更多更丰富,页面难免会大而“重”,首屏作为用户与网站的第一眼互动,“秒开”变得至关重要。今天就来谈谈如何对首屏时间进行性能优化。

  • 什么是首屏?

以800×600像素尺寸为标准,当浏览器加载页面后看到第一眼的显示内容为首屏;从开始加载到浏览器页面显示高度达到600像素且此区域有内容显示的时间为首屏显示时间(当然这个标准随着技术进步在不断变化)下图京东首页为例,对于电商网站而言,营销活动的banner、轮播图、账户信息、分类检索等供用户自主选择的主要内容全部集中在首屏,所以对首屏的加载速度要求极高。

  • 大公司是如何进行首屏优化的?

仍然以京东为例:

当我们打开京东的网站(不要滚动鼠标和键盘),右键查看源代码会发现京东首页的DOM树出奇的简单,页面DOM中多含有mod_lazyload的类。

<div class="J_f J_lazyload J_f_lift mod_lazyload need_ani chn" id="portal_8" data-backup="basic_8" data-source="cms:basic_8" data-tpl="portal_tpl">

再看下 localstorage

尤其在查看键名类似于 URL 的键值对时,会发现它们的值中多存在一串完整的类似于html的内容。

{"dom":"{%var liftTxtArr = pageConfig.liftTxtArr,clstagPrefix = 'h|keycount|2016|41a', i;%}<ul class=\"lift_list\">{% for (i = 0; i < liftTxtArr.length; i++) { %}<li class=\"J_lift_item lift_item{%= (i===0) ? ' lift_item_first' : '' %}\" clstag=\"{%= clstagPrefix + (i < 9 ? '0' : '') + (1+i) %}\"><a href=\"javascript:;\" class=\"lift_btn\"><span class=\"lift_btn_txt\">{%= liftTxtArr[i] %}</span></a></li>{% } %}<li class=\"J_lift_item J_lift_item_top lift_item lift_item_top\" clstag=\"{%= clstagPrefix + (i < 9 ? '0' : '') + (1+i) %}\"><a href=\"javascript:;\" class=\"lift_btn\"><span class=\"lift_btn_txt\">顶部<i class=\"lift_btn_arrow\"><!--&#xe606;--></i></span></a></li></ul>","style":".lift{display:none;position:fixed;z-index:100;-moz-box-shadow:0 0 4px rgba(0,0,0,.2);box-shadow:0 0 4px rgba(0,0,0,.2);background-color:#918888}.lift_item{border-top:1px solid #b1aaaa;-webkit-transition:background-color .1s;-moz-transition:background-color .1s;transition:background-color .1s}.lift_item_first{border-top:none}.lift_btn{*cursor:pointer;overflow:hidden;display:block;width:24px;padding:10px 5px;line-height:14px;text-align:center;color:#fefefe;-webkit-transition:color .1s;-moz-transition:color .1s;transition:color .1s}.lift_item:hover,.lift_item_on{position:relative;border-bottom:1px solid #d70b1c;margin-bottom:-1px;border-color:#d70b1c;background-color:#d70b1c}.lift_item:hover .lift_btn,.lift_item_on .lift_btn{color:#fff}.lift_item_top,.lift_item_top:hover{border-top:1px solid #f6f6f6;background-color:#5e4a4a;border-bottom:0;margin-bottom:0}.lift_item_top .lift_btn{color:#fff}.lift_btn_arrow{display:block;width:0;height:0;margin:auto;border-style:solid;border-width:4px;border-color:#5e4a4a #5e4a4a #fff}.o2_mini .lift{display:none}","version":"062a40dffff4da26"}

由上面的结构可知,京东已经将它们的页面结构放到了localstorage。由此可见,只是它页面其中一个模块的内容。分析到这里已经很明显了,京东通过前端缓存和异步加载已经完美的实现了首屏快速加载,在Web端达到了秒开的级别。

京东首先只加载框架然后将框架内容与模版绑定,用户滚动时,一旦该框架内容部分进入了视窗,则请求对应的 data-tpl 地址,拿到渲染这个模块所需要的脚本和数据,不过这中间还有一层本地缓存 localstorage,如果在本地缓存中匹配到了对应的 hash string 内容,则直接渲染,否则请求到数据之后更新本地缓存。localstorage 中的 version 会在页面加载时候,与后端文件 hash相对比,hash不变直接取localstorage中的内容(当然也可以使用cookie判断版本)。

如果数据和初始化脚本包装在一起,虽然节约了一个请求,但一旦数据变化,整个脚本都得重新加载。将数据和脚本分离,脚本可以长期缓存在本地,单独请求数据,这个量会小很多。直接改变上面的 version 版本号便可以让浏览器重新请求最新脚本。

从上面可以看出,任何一个模块的改动,在前端只会引起一个较小的加载变化,加上 http 的缓存策略,服务器的压力也是很小的。

看了上面的内容,相信大家已经对京东关于首屏优化的方案有了一个大致的了解,下面我们再整理一下关于首屏显示速度优化细节上的内容:

  • CSS静态文件在哪里?

为了追求速度,京东的首页是没有 CSS 外链的,相信对于我们来说,也是老生常谈的前端优化了。

有人可能会问,如果没有 CSS 外链,那一整个页面的 CSS 是否会增加页面的体积?其实上面已经提到,将页面切分为模块化加载,对应模块下的 CSS 交给 js 或 jsonp 请求返回。

  • js文件怎么加载?

如果外部 CSS 资源较小,可以直接将这些内容插入到HTML文档中,这称为“内嵌”。通过这种方式内嵌较小 CSS 资源,浏览器可以继续呈现网页。请注意,如果CSS文件较大,完全内嵌CSS可能会对浏览器构建和渲染DOM树增加不少压力。如果 CSS 文件较大,您需要识别和内嵌呈现首屏内容所需的 CSS,并暂缓加载其余样式,直到首屏内容显示之后为止。

京东采用请求的方式减少了与服务器交互的时间。

<script src="//misc.360buyimg.com/??/jdf/lib/jquery-1.6.4.js,/jdf/2.0.0/ui/ui/1.0.0/ui.js,/mtd/pc/index/gb/lib.min.js,/mtd/pc/base/1.0.0/base.js,/mtd/pc/common/js/o2_ua.js,/mtd/pc/index/home/index.min.js,/mtd/pc/index/home/init.min.js"></script>
  • js文件怎么执行?

懒加载也就是延迟加载,有交互才执行。js文件有需要时才加载,并不是再打开页面时一次性全部加载完成。

  • 图片如何处理?

图片在其他屏(非首屏)都采用懒加载的模式,这样既能节省流量,也能减少请求数或延迟请求数。

当访问一个页面的时候,先把 img 元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,俗称占位图),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。用这样的方式,可以使页面加载速度变快,从而减轻服务器的压力,同时优化用户体验。当访问一个页面的时候,先把 img 元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,俗称占位图),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。用这样的方式,可以使页面加载速度变快,从而减轻服务器的压力,同时优化用户体验。

  • 服务器需要做什么?
  1. 少量静态文件的域名,图片与iconfont均是放在同一个域下,减少DNS的解析事件,最好做到域名收敛。
  2. 模块化接口的支持。
  3. 首屏内容最好做到静态缓存。

版权声明:

*本文在转载时有删改

原作者:一半水一半冰

出处:https://www.cnblogs.com/jingh/p/6531105.html#3962455