本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-07-04
本文摘抄自CSS世界:读书笔记
块状化的意思是,元素一旦 float
的属性不是 none
,则其 display
计算值就是 block
或者 table
(只有原本的值是inline-table
,设置 float
后才会变为 table
)。
因此,没有任何理由出现下面的样式组合。
span {
display: block; /* 多余 */
float: left;
vertical-align: middle; /* 多余 */
}
float
属性的原本作用“只是为了实现文字环绕效果”,float
造成高度塌陷只是让跟随的内容可以和浮动的元素在一个水平线上,还有一点就是,行框盒子如果和浮动元素的垂直高度有重叠,则行框盒子在正常定位状态下只会跟随浮动元素,而不会发生重叠。示例如下:
很多人会有这种想法,就是认为一个元素只要设置了具体的高度值,就不需要担心 float
属性造成的高度塌陷问题了,虽然是对的,但是也包含着陷阱,因为“文字环绕效果” 是由两个特性 (即 “父级高度塌陷” 和 “行框盒子区域限制”)共同限制的结果,定高只能解决 “父级高度塌陷” 带来的影响,但是对行框盒子区域限制却没有任何效果,结果导致的问题是浮动元素垂直区域一旦超出高度范围,或者下面元素的 margin-top
负上偏移,就很容易使后面的元素发生“环绕效果”,请看7-1示例 的第4、5个示例。
其中第4例中,父元素 .father
设置了定高, 浮动元素 .float
因为内联图片的底部空隙问题高度大于了定高,就造成了后面的第二行的行框盒子的“环绕效果”。
clear
属性只有块级元素才有效,所以使用伪元素清浮动的时候要设置 display
为 block/table/list-item
。
clear
属性是让自身不能和前面的浮动元素相邻,也就是说 clear
属性对 “后面的” 浮动元素是不闻不问的。
举个例子,假设容器宽度足够宽,有10个 <li>
元素,设置了如下 css 代码:
li {
width: 20px;
height: 20px;
float: left
}
li:nth-of-type(3) {
clear: both;
}
只有第三个 <li>
设置了清浮动,所以前两个一行显示,而后面的第4个及以后的 <li>
并没有被 第3个 <li>
的清浮动影响到,依然跟随着一行显示。
BFC(block formatting context),中文为 “块级格式化上下文”,可以理解为“CSS世界的结界”,表现原则为:如果一个元素具有BFC,内部子元素再怎么翻江倒海,都不会影响外部的元素,所以,BFC元素不可能发生 margin
重叠,因为 margin
重叠会影响外面的元素;BFC 元素也可以用来清除浮动的影响,因为如果不清除,子元素浮动则父元素高度塌陷,必然会影响后面元素布局和定位。
那什么时候会触发BFC呢?常见的情况如下:
<html>
根元素float
的值不为 none
overflow
的值为 auto/scroll/hidden
display
的值为 table-cell/table-caption/inline-block
position
的值不为 relative
、static
只要元素符合上面任意一个条件,就无需使用 clear:both;
属性去清楚浮动的影响了。
理论上,任何 BFC 元素和 float 元素相遇的时候,都可以实现自动填充的自适应布局,但是,由于绝大多数的触发 BFC 的属性自身有一些古怪的特性,所以,实际操作的时候,能兼顾流体特性和 BFC 特性来实现无敌自适应布局的属性并不多。
1、float:left
浮动元素本身BFC化,然而浮动元素有破坏性和包裹性,失去了元素自身的流体适应性,因此无法用来实现自动填满容器的自适应布局。
2、position: absolute
绝对定位,脱离文档流严重,和非定位元素很难玩到一块儿去。
3、overflow: hidden
不像浮动和绝对定位,其本身还是一个很普通的元素,因此,块状元素的流体特性保存的非常好,附上BFC的独立区域特性,所以可以作为作用的BFC布局属性使用的,唯一问题就是容器盒子外的元素可能会被隐藏掉。
4、display: inline-block;
它会让元素尺寸包裹收缩,不再拥有水平的流动特性。
5、display: table-cell;
跟 inline-block
一样,它会跟随内部元素的宽度显示,但是,table-cell
有一个非常神奇的特性,就是宽度值设置得再大,实际宽度也不会超过表格容器的宽度,所以可以把 display: table-cell
这个BFC元素宽度设置得很大,比如9999px,就能跟 block
水平元素自动适应容器空间效果一模一样了。
.bfc {
display: table-cell;
width: 9999px;
}
/* 需要IE8+ 二是应付连续英文字符换行有些吃力 */
最后,我们可以提炼出两套IE7及以上版本浏览器适配的自适应解决方案。
/* 借助 overflow: hidden 属性 */
.lbf {
overflow: hidden;
}
/* 融合 display: table-cell / inline-block */
.lbf {
display: table-cell;
width: 9999px;
/*
hack 写法
IE6识别*和_
IE7识别*, 不识别_
如果不需要兼容IE7可以忽略
使用 inline-block,是因为在IE6和IE7 浏览器下
block水平的元素设置 display: inline-block 元素还是 block 水平
还是会自适应容器的可用宽度显示
所以可以用来兼顾实现流体布局和BFC特性
*/
*display: inline-block;
*width: auto;
}
需要注意的是,这两种均有一点不足,前者如果子元素要定位到父元素的外面可能会被隐藏,后者要让连续英文字符换行有点吃力,所以应该根据实际的项目场景选择合适的技术。
要想彻底清除浮动的影响,最适合的属性不是 clear
而是 overflow
,一般使用 overflow: hidden
,利用 BFC 的结界特性彻底解决浮动对外部或兄弟元素的影响。
当给设置overflow:hidden
时,实际上创建了一个BFC结界,此属性反过来决定了 height:auto
是如何计算的。在“BFC布局规则”中提到:计算BFC的高度时,浮动元素也参与计算。因此,父元素在计算其高度时,加入了浮动元素的高度,“顺便”达成了清除浮动的目标,所以父元素就包裹住了子元素。
一个设置了 overflow: hidden
声明的元素,假设同时存在 border
属性和 padding
属性,则当子元素内容超出容器宽度高度限制的时候,剪裁的边界是 border box 的内边缘,而非 padding box 的内边缘。
overflow
属性存在一个很经典的不兼容问题,即 Chrome 浏览器,如果容器可以滚动(假设是垂直滚动),则 padding-bottom
也算在滚动尺寸之内,而在IE和 Firefox 浏览器则会忽略。所以我们在实际项目开发的时候,要尽量避免滚动容器设置 padding-bottom
值,除了样式表现不一致外,还会导致 scrollHeight
值不一样,这往往会给开发带来难以察觉的麻烦,需要引起注意。
两者支持的属性与 overflow
属性一致:
visible
:默认值,内容溢出显示。hidden
:剪裁scroll
:滚动条区域一直在auto
:不足以滚动时没有滚动条,可以滚动时滚动条出现。如果 overflow-x
和 overflow-y
属性中的一个值设置为 visible
而另外一个设置为 scroll/auto/hidden
,则 visible
的样式表现会如果 auto
,除非两者都为 visible
。
即一个为visible
,一个 为 hidden / auto / scroll
, 则设置 visible
会被当做 auto
来处理,如下,效果一致
但是,scroll
、auto
和 hidden
这3个属性是可以共存的。
HTML 中有两个标签是默认可以产生滚动条的,一个是根元素 <html>
,另一个是文本域 <textarea>
。
在PC端,无论是什么浏览器,默认滚动条均来自 <html>
,而不是 <body>
标签。所以,如果我们想要去除页面默认的滚动条,只需要:
html {
overflow: hidden;
}
/* 没必要把 body 也拉下水 */
当然,这一点只对PC端有效,对于移动端并不一定有效。还有就是在PC端,窗体滚动高度可以使用 document.documentElement.scrollTop
获取,但是在移动端,可能就要使用 document.body.scrollTop
获取。
PC端滚动条会占用容器的可用宽度或高度。但是在移动端,滚动条一般都是悬浮模式,不会占据可用宽度。要知道自己浏览器的滚动栏宽度是多少,可以使用以下代码:
<style>
.box {
width: 400px;
overflow: scroll
}
</style>
<div class="box">
<div id="in"></div>
</div>
<script>
console.log(400 - document.getElementByID("in").clientWidth);
</script>
滚动栏占据宽度的特性有时会给我们的布局带来不小的麻烦,如我们希望实现一个表格头固定、表格主体可以滚动的效果,常见的实现方法是使用双 <table>
,一个用来展示表头,一个用来展示主体并设置 overflow: auto
。但是当主体出现滚动条的时候,主体可用宽度被压缩,表格列往往无法完美对齐。
常用的解决方法有:
<table>
元素使用固定的宽度值,但是距离右侧留有滚动条宽度的间隙另一个问题就是,页面加载的时候水平居中的布局可能会产生晃动,因为窗体默认是没有滚动条的,而 HTML 内容是自上而下加载的,就会发生一开始没有滚动条,后来突然出来滚动条的情况,此时页面的可用宽度发生变化,水平居中重新计算,导致页面发生晃动。解决方法如下:
html {
overflow-y: scroll; /* for IE8*/
}
:root {
overflow-y: auto;
overflow-x: hidden;
}
:root body {
position: absolute;
}
body {
width: 100vw;
overflow: hidden;
}
滚动条样式也可以自定义,其中相关的CSS属性如下:(针对Chrome浏览器)
::webkit-scrollbar
::webkit-scrollbar-button
::webkit-scrollbar-track
::webkit-scrollbar-track-piece
::webkit-scrollbar-thumb
::webkit-scrollbar-corner
依赖overflow
的实现的css效果,其中就有单行溢出和多行溢出 … 显示。
示例如下:
核心代码
.box {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
对-webkit-
私有前缀支持良好的浏览器还可以实现多行文字打点效果。
.box2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
基于URL地址的锚链(如#abc,可以使用location.hash
获取),实现锚点跳转的方法有两种,一种是<a>
标签的name/href
属性,还有一种就是使用标签的id
属性。
<a href="#abc">标题1</a>
<a name="abcd">标题2</a>
<a id="abcde">标题3</a>
1、锚点定位行为的触发条件
有以下两种情况可以触发:
如果我们的锚链就是一个很简单的#
,则发生定位行为时,页面是定位到顶部的,所以一般实现返回顶部效果都是使用如下HTML:
<a href="#">返回顶部</a>
“focus 锚点定位” 指的是类似链接或者按钮、输入框等可以被focus的元素被focus时发生的页面重定位现象。
在PC端,我们使用Tab键可快速定位可focus的元素,此时如果该元素在可视范围之外,浏览器就会自动重新定位。
两种定位方法的表现的差异是,“URL 地址锚链定位” 是让元素定位在浏览器窗体的上边缘,而 “focus 锚点定位” 是让元素在浏览器窗体范围内显示即可,不一定在上边缘。
2、锚点定位作用的本质
锚点定位行为的发生,本质上是通过改变容器滚动高度或者宽度来实现的,需要注意的是这里说的是容器的滚动高度。
首先,锚点定位也可以发生在普通的容器元素上,而且定位的发生是由内而外的,例如,我们页面上有一个div
元素设置了overflow: auto
且子元素高度超出其自身高度限制:
<style>
.box {
height: 120px;
border: 1px solid #bbb;
overflow: auto;
}
.content {
height: 200px;
background: #eee;
}
</style>
<div class="box">
<div class="content"></div>
<h4 id="title">底部标题</h4>
</div>
<p>
<a href="#title">点击测试</a>
</p>
由于.content
元素高度超过了容器,因此h4远古三起初是不可见的,当点击下方a标签锚点,则滚动条位置变化了,实际是改变了scrollTop
值。此时h4元素则自动出现了。
“由内而外”指的是,普通元素和窗体同时可滚动的时候,会由内而外触发所有的可滚动窗体的锚点定位行为。假设我们的浏览器也是可以滚动的,则点击a标签时,先触发.box容器的锚点定位,也就是滚动到底部,然后再触发窗体的锚点定位,“底部标题”和浏览器窗口的上边缘对齐。
其次就是设置了 overflow: hidden
的元素也可滚动的,说干脆点就是 overflow: hidden
跟 overflow: auto
和 overflow: scroll
的差别就在于有没有那个滚动条,元素设置了 overflow: hidden
,里面内容高度溢出的时候,滚动仍然存在,仅仅滚动条不存在。
虽然设置了 overflow: hidden
且高度溢出,鼠标无论怎么滚,都没有滚动行为发生,但是如果发生锚点定位,滚动依然会发现。
比如将7-6示例中.box元素的overflow: auto
改为 overflow: hidden
,然后点击a便签进行锚点定位,就会发现滚动还是发现了。如下示例:
所以,锚点定位的本质上改变了 scrollTop
或 scrollLeft
值,因此上面的定位效果等同于执行以下js代码:
document.querySelector('.box').scrollTop = 200;
则可以理解为,浏览器的锚点定位实现了类似JavaScript的效果,那么,实现选项卡切换时,也可以使用锚点定位来实现。
<style>
.box {
height: 10em;
border: 1px solid #ddd;
overflow: hidden;
}
.list {
line-height: 10em;
height: 10em;
background: #ddd;
text-align: center;
color: red;
}
.link {
margin-top: 20px;
text-align: center;
}
.link a {
outline: 1px solid #ddd;
padding: 10px 15px;
margin-right: 10px;
}
</style>
<div class="box">
<div class="list" id="one">1</div>
<div class="list" id="two">2</div>
<div class="list" id="three">3</div>
<div class="list" id="four">4</div>
</div>
<div class="link">
<a href="#one">1</a>
<a href="#two">2</a>
<a href="#three">3</a>
<a href="#four">4</a>
</div>
容器设置了 overflow: hidden
,且每个列表高度和容器的高度一样高,这样就保证永远只显示一个列表,当我们点击a标签进行锚点定位,会改变URL的锚链,从而触发对应id的列表发生锚点定位,也就是改变容器滚动高度让对应列表的上边缘和滚动容器的上边缘对齐。
但这种方案有两个不足之处:
解决方案就是,通过 foucs 锚点定位
,上面说过,只要定位的元素在浏览器窗体内中,就不会触发窗体的滚动,也就解决了选项卡切换的时候页面不会发生跳动。
<style>
.box {
height: 200px;
border: 1px solid #ddd;
overflow: hidden;
}
.list {
position: relative;
height: 100%;
background: #ddd;
text-align: center;
color: red;
}
.list input {
position: absolute;
top: 0;
height: 100%;
width: 1px;
border: 0;
padding: 0;
margin: 0;
clip: rect(0 0 0 0);
}
.link {
margin-top: 20px;
text-align: center;
}
.link label {
outline: 1px solid #ddd;
padding: 10px 15px;
margin-right: 10px;
}
</style>
<div class="box">
<div class="list"><input id="one">1</div>
<div class="list"><input id="two">2</div>
<div class="list"><input id="three">3</div>
<div class="list"><input id="four">4</div>
</div>
<div class="link">
<label class="click" for="one">1</label>
<label class="click" for="two">2</label>
<label class="click" for="three">3</label>
<label class="click" for="four">4</label>
</div>
原理就再每个列表里塞一个看不见的input
元素,然后选项卡按钮变成label
元素,并通过for
属性与input
输入框的id相关联,这样点击就会触发输入框的focus行为,触发锚点定位,实现选项卡切换效果。
然而,使用上述这种技术在实际项目中使用还离不开 JavaScript 的支持,一个是选项卡按钮的选中效果,另一个就是处理列表部分区域部分在浏览器视口外时依然会跳动问题,相关类似以下做法:
window.onload = function () {
var linkEl = document.querySelector('.link');
linkEl.onclick = function (e) {
if (e.target && e.target.classList.contains('click')) {
e.target.removeAttribute('for');
document.querySelector('.box').scrollTop = (+e.target.innerText - 1) * 200;
}
}
}
即绑定点击事件,手动定位.box
容器的scrollTop
,并且移出label
的for
属性,不然还是会发生锚点定位。这样的好处是,即使js没有被加载完,选项卡依然的可用的,当js加载完后,就体验更好了。同样这一技术只适合于高度固定的选项卡效果。
时刻牢记 overflow: hidden
元素依然可用滚动这一点,可用让我们以更简单的方式实现一些交互效果,比如实现自定义的滚动条效果,传统实现都是父元素设置overflow: hidden
,然后子元素使用一个大的div
包起来,设置绝对定位,然后监听父元素的"scroll"事件,改变top
值,或者使用transform
进行偏移子元素以及自定义滚动条的位置。
现在,根据overflow: hidden
元素依然可滚动这一点,完全没必要使用多于的div
包裹子元素,可以直接放在父元素,然后监听父元素的"scroll"事件,手动改变父元素的scrollTop
和自定义滚动条的位置,这样的好处就是,无须做偏移边界判断,可与元素的"scroll"事件以及scrollTop
实时获取,天然集成。
与 float
的块状化一样,元素一旦 position
属性值为 absolute
或 fixed
,其 display
计算值就是 block
或者 table
。
一个绝对定位元素,没有任何的left/top/right/bottom
属性设置,并且其祖先元素全部都是非定位元素,其位置在哪里?
很多人都认为会在浏览器窗口左上方。实际上,还是在当前位置。
正是这种错误认知导致凡是使用 absolute
绝对定位的地方,一定父容器 position: relative
,同时 left/top
等属性定位,甚至必须使用 z-index
属性设置层级,这是非常严重的认知和使用错误,因此要牢记 absolute
是非常独立的CSS属性值,其样式和行为表现不依赖其他任何CSS属性就可以完成。
如示例页面7-11所示,在容器左上角定位一个标签,只需要使用 position: absolute
即可,无需设置父元素,也无需定位 left: 0; top: 0;
。
再比如,我们经常会在消息导航栏的右上方设置一个显示数量的小图标,完全也只需要使用 position: absolute
再加上 margin
属性进行定位即可,无需设置其他。如示例页面7-12
.icon-new {
position: absolute;
width: 20px;
height: 20px;
background: red;
color: white;
border-radius: 50%;
margin: -9px 0 0 2px;
}
虽然说,position: absolute
后的 display
计算值都是块状的,但是其定位的位置和没有设置 absolute
时候的位置相关,比如相面两端HTML代码:
<h3>标题<span class="follow">span</span></h3>
<h3>标题<div class="follow">div</div></h3>
其差别在于,“标题”文字的后面跟随的标签,一个是内联的 span
,还有一个是块状的 div
,此时显然 span
字符跟随在“标题”的后面显示,div
字符则换行在 “标题”下面显示。
但如果,选择设置如下样式:
.follow {
position: absolute;
}
此时,span
和 div
的 display
计算值都是 block
,但是它们的位置,还是和没有应用 absolute
的时候一样,一个在后面,一个在下面,如示例页面7-13
所以说,设置position: absolute
后其定位的位置和没有设置 absolute
时候的位置相关。
按道理讲,absolute
和 float
一样,都可以让元素块状化,应该不会受控制内联元素对齐的 text-align
属性影响,但是结果却出人意料,text-align
可以改变 absolute
元素的位置。
如下HTML代码:
<style>
p {
text-align: center;
}
img {
position: absolute;
}
</style>
<p>
<img src="1.jpg" />
<p>
如示例页面7-14展示,图片确实受到了 text-align
属性影响在中间位置显示了,但并不是水平居中,仅仅是在中间区域而已,而这种情况,但是并不是 text-align
和 absolute
元素直接发生关系,absolute
元素的 display
计算值是块状的,text-align
是不会有作用的,之所以会产生变化,本质上是 “幽灵空白结点” 和 “无依赖绝对定位” 共同作用的结果。
具体的渲染原理如下:
1)、由于 img
是内联水平,p
标签中存在一个宽度为0,看不见摸不着的 “幽灵空白结点”,也是内联水平,于是受 text-align: center
影响而水平居中显示。
2)、img
设置了 absolute
表现为 “无依赖绝对定位”,因此在“幽灵空白结点”后面定位显示;同时由于图片不占据空间,这里的“幽灵空白结点”当仁不让,正好在p
元素水平中心位置显示,于是我们看到了图片从p
元素水平中间位置显示的效果。
此时,我们只要 margin-left
一半图片宽度负值大小,就可以实现图片的水平居中效果了。
当absolute
遇到left/top/right/bottom
属性的时候,absolute
元素才真正变成绝对定位元素,而它的流体特性(自动填充宽度或高度),不是默认就有的,而是在特定条件下才具有,这个条件就是“对立方向同时发生定位的时候”。
其中left
和right
属于水平对立定位方向,而top
和bottom
属于垂直对立定位方向,即当一个绝对定位元素,其对立定位方向属性同时具有定位数值的时候,流体特性就发生了。
<div class="box"></div>
<style>
.box {
position: absolute;
left: 0;
right: 0;
}
</style>
如果只有left
或只有right
属性,则由于包裹性,.box
宽度为0,但是在本例中,因为left
和right
同时存在,所以宽度就不是0,而是表现为“格式化宽度”,宽度大小自适应于.box
包含块的 padding box,也就是说,如果包含块padding box发生变化,.box
的宽度也会跟着一起变。
所以,不建议使用以下写法来实现元素与父元素同样大小。
/* 写法1 */
.box {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
这样写的话,当如果此时为box
加上padding
,则此时的尺寸为100% + 2 * padding
,可能有人会想到使用box-sizing: border-box
这样确实可以让保持宽度仍是100%,但是如果添加的是margin
呢,如果使用以下代码,
/* 写法2 */
.box {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
会自动在上下左右留白对应的margin
,但如果按照写法1的实现,会发现其布局已经跑到包含块的外面去了。
最常用的,还是使用写法2之后,可以使用margin: auto
就实现水平垂直居中,因为此时,绝对定位元素在水平和垂直方向上都有了流体特性。
relative
的定位是相对自身原来的位置,并且不会影响周围其他元素的布局,同时需要注意的是,相对定位元素的left/top/right/bottom
的百分比值是相对于包含块计算的,而不是自身。
top
和bottom
这两个垂直方向的百分比计算跟height
的百分比值是一样的,都是相对高度计算的,同时,如果包含块的高度是auto
,那么计算值是0,偏移无效。
当相对定位同时应用对立方向定位值时,也就是top/bottom
和left/right
同时使用的时候,其表现和绝对定位差异很大。绝对定位是尺寸拉伸,保持流体特性。但是,相同定位却是“你死我活”的表现,也就是说,只有一个方向的定位属性会起作用,默认的文档流是自上而下,从左往右,因此 top/bottom
同时使用时,bottom
无效;left/right
同时使用的时候,right
无效。
蒙层弹窗是网页中常见的交互,其中黑色半透明全屏覆盖的蒙层基本上都是使用fixed
定位实现的,但是,如果细致一点就会发现蒙层无法覆盖浏览器右侧的滚动栏,并且鼠标滚动的时候后面的背景内容依然可用被滚动,并没有被锁定。
要解决一个问题,可以从发生这个问题的原因入手,position: fixed
蒙层之所以出现背景依然滚动,那是因为滚动的元素是根元素,正好是fixed
定位元素的包含块,所以,如果希望背景被锁定,可以让页面的滚动条是由内部的普通元素产生而不再是根元素产生滚动即可。
html, body {
height: 100%;
overflow: hidden;
}
.page {
height: 100%;
overflow: auto;
}
但如果网站的滚动结构不方便调整,则需要借助JavaScript来实现锁定。
如果是移动端项目,阻止touchmove
事件的默认行为可以防止滚动;如果是桌面端项目,可以让根元素直接overflow: hidden
,但是,Windows操作系统下的浏览器的滚动条都是占据一定宽度的,滚动条的消失必然会导致页面的可用宽度变化,页面会产生体验更糟糕的晃动问题,所以需要找个东西来填补消失的滚动条,这时候可以使用透明的border来模拟填补。
于是,在蒙层显示的同时执行下面的JavaScript代码:
// 滚动栏的宽度 先设置一个默认值
var withBar = 17;
var root = document.documentElement;
if (typeof window.innerWidth === 'number') {
widthBar = window.innerWidth - root.clienWidth;
}
root.style.overflow = 'hidden';
root.style.borderRight = widthBar + 'px solid transparent';
隐藏时,则执行如下代码:
var root = document.documentElement;
root.style.overflow = '';
root.style.borderRight = '';