温馨提醒
基础页面开发
盒模型
1 概念
页面中的每个元素都可以用一个矩形盒子表示,矩形盒子用一个模型来描述其占用的空间,这个模型就称为标准盒模型。简单说,盒模型是用来描述元素在页面中占用的空间的模型。
2 构成
3 分类
(元素宽度)盒子宽度 = content-width + padding-width + border-width,也就是说,我们在元素上设置的CSS属性 width 值 只是内容区域上的宽度。高度同理。
div {
box-sizing: content-box;
width: 100px; /* 内容区域的宽度 */
padding: 10px;
border: 10px;
margin: 10px;
}
上面代码中,浏览器计算得出 div 元素宽度为 120px 。
这意味着当你调整一个元素的宽度和高度时需要时刻注意到这个元素的边框和内边距。当我们实现响应式布局时,这个特点尤其烦人。因此实际开发中都使用IE盒模型。
- IE盒模型
(元素宽度)盒子宽度 = width = content-width + padding-width + border-width,也就是说,我们在元素上设置的CSS属性 width 值 就是元素的宽度,它包括了内容区域的宽度+边框宽度+内边距宽度。高度同理。
div {
box-sizing: border-box;
width: 100px; /* 元素的宽度 */
padding: 10px;
border: 10px;
margin: 10px;
}
上面代码中,浏览器计算得出 div 元素宽度为 100px 。content-width 为 80px 。
注意,盒模型的宽高计算不包括外边距。
实际开发中将 box-sizing
设置为 border-box
。
*, *:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
5 图示对比
在同样的设置下,元素宽高差别很大。点击这儿。
布局
网页的元素可以看作一个个盒子,它们之间的排列组合就形成了网页的布局。以下是常见的网页布局方式。
1 普通文档流布局
默认的布局方式,由块级元素(display: block
)和行内元素(display: inline
)等组成。行内元素在一行内按照从左往右的顺序排列,块级元素独占一行按照从上到下的顺序排列。
2 浮动布局
相对于普通文档流布局,浮动布局会脱离普通文档流,分为左右浮动。在实际开发中,使用了浮动布局后要清除浮动,使浮动元素回到普通文档流布局中。可以考虑使用清除浮动。
.ele {
float: left|right;
}
主流的清除浮动方法(背):当子元素全是浮动元素时,在父元素上加clearfix类,使用::after伪元素清除所有浮动。不理解请查看清除浮动。
<div class="clearfix">
<div class="c1" style="float: left;"></div>
<div class="c2" style="float: left;"></div>
<div class="c3" style="float: right;"></div>
</div>
.clearfix::after{ /* ::after 伪元素是CSS3写法,:after 是CSS2写法 */
content: '';
display: block; /* 或者 table*/
clear: both;
}
.clearfix{
zoom: 1; /* IE 兼容*/
}
3 定位布局
根据 定位属性 position
的值不同,定位布局分为相对定位,绝对定位,固定定位,粘性定位。
position: static | relative | absolute | fixed | sticky ;
position
默认属性值为 static
, 可以理解为未设置定位属性,元素的位置就是在普通文档流中的位置。
- 相对定位
元素使用 position: relative;
属性进行相对定位。使用相对定位的元素不会脱离普通文档流。元素先放置在未添加定位时的位置(在普通文档流中的位置),其定位是参考自身未定位时的位置,再在不改变页面布局的前提下根据 top|bottom|left|right
属性值调整位置(因此会在此元素未添加定位时所在位置留下空白,即为元素预留出空间)。可以使用 z-index
属性设置层叠级。点击查看演示。
- 绝对定位
元素使用 position: absolute;
属性进行绝对定位。使用绝对定位的元素会被移出(脱离)普通文档流,并且不为元素预留空间。其定位是参考最近的 position
为非 static
值的祖先元素,根据 top|bottom|left|right
属性值调整位置。可以使用 z-index
属性设置层叠级。
如果绝对布局元素的祖先元素都没有设置定位属性,absolute
元素将参考body
作为坐标原点进行定位。点击查看代码演示。
- 固定定位
元素使用 position: fixed;
属性进行固定定位。元素会被移出正常文档流,并不为元素预留空间。定位参考屏幕视口(viewport),根据 top|bottom|left|right
属性值计算调整位置。元素的位置在屏幕滚动时不会改变。固定定位类似绝对定位,只是参考基准不同。
fixed
属性会创建新的层叠上下文。(????)
当元素祖先的 transform
, perspective
或 filter
属性非 none
时,容器由视口改为该祖先。
- 粘性定位
元素使用 position: sticky;
属性进行粘性定位。元素根据正常文档流进行定位,定位参考它的最近滚动祖先,使用 top|bottom|left|right
属性设置阙值。注意,必须指定一个阙值才能使粘性布局生效,否则行为同相对布局。
该值总是创建一个新的层叠上下文。
粘性定位可以被认为是相对定位 relative 和固定定位 fixed 的混合。元素在跨越特定阈值前为相对定位,之后为固定定位。
粘性定位常用于定位字母列表的头部元素。点击查看演示字母表滚动。
4 弹性布局
也称 Flex 布局,是一个完整的模块,而不是一个单一属性,其中有的属性是设置在父元素上,有些则是设置在子元素上。点击参考笔记。
5 网格布局
是用于制定行与列的二维 CSS 布局方法,可以将页面分割成数个主要的区域,或者用来定义组件内部元素间的大小、位置和图层之间的关系。点击参考笔记。
CSS命名规范-BEM
在编写CSS代码时要注意类名的唯一性、可读性和可维护性。唯一性为了避免样式冲突,可读性和可维护性为了后期阅读管理CSS样式代码。编写可用的CSS容易,但是CSS代码的组织管理非常不容易。
BEM 是什么
BEM 是一种基于组件的CSS命名方法,它的基本思想是将用户界面划分成独立的模块,模块名是唯一的。 Block (模块)、Element (元素)、Modifier(修饰符)。使用BEM命名方式让CSS类名更具语义化,有更高的开发友好性。
Block - 模块,名字的单词之间用
-
符号连接。 Element - 元素,模块中的元素本身,用__
符号连接 Modifier - 修饰符,表示元素的状态,用--
符号连接
BEM 规定的CSS类名命名规范:block-name__element-name--modifier-name
。
BEM优点
- 保证了CSS类名的唯一性
因为在项目中,组件是唯一的,组件名也是唯一的,相应地要求模块名必须是唯一的。BEM 这种组件化的命名方法可以保证CSS类名的唯一性。
- 提高了CSS类名的可读性和可维护性。
CSS类名模块语义化了,便于后期的维护。
- 减少了 CSS 类名的层层嵌套,提升了网页的渲染效率。
BEM缺点
将 BEM 用于中大型项目之后,我们会发现,当嵌套的层级越多时,CSS类名也会越长,这给编写 HTML 代码带来了一些麻烦,同时也增加了 HTML 的文件大小。因此,很多开发团队在制定CSS命名规范时,吸收BEM思想,但并不完全按照BEM的命名规范(block-name__element-name--modifier-name
),比如
/* Instagram团队使用的驼峰式 */
.blockName-elementName--modifierName { /* ... */ }
/* 单下划线 */
.block-name_element-name--modifierName { /* ... */ }
其实这对缩短CSS类名没有很大的帮助,只能本着尽量精简的原则,为了提高CSS样式代码可读性和后期可管理性,我们已经牺牲了编写简单的类名。
使用 BEM 时的一些技巧和注意事项
- ClassName 的命名应该尽量精短、明确有语义,以英文单词命名,避免意义不明的缩写。
- 注意,BEM 命名是不考虑结构的,也就是说 一个元素上的CSS类名只包括 模块名,元素本身名字和修饰符名字,不包括该元素的祖先元素名。当我们遵循了这个规定,无论父元素名发生改变,或是模块构造发生的改变,还是元素之间层级关系互相变动,这些都不会影响元素的类名。
- BEM配合CSS预处理语言一起使用,如SCSS,可以解决手写冗长CSS类名的繁琐。
- 虽然 BEM 命名冗长,但也无需担心文件体积的问题,由于服务端有gzip压缩,BEM命名相同的部分多,压缩下来的体积不会太大。另外现在都用IDE来编写代码了,有自动提示功能,也无须担心重复的输入过长的名字。
- BEM禁止使用子代选择器。子代选择器存在选择器权重问题,一次开发后,后期可能存在样式权重高覆盖前面的样式、层次关系太长后期维护和修改工作量庞大 。
因为BEM命名长,就用子代选择器来代替BEM命名?这样至少在HTML编写时,让HTML标签看起来美观一点。NO! 当我们用子代选择器来定位元素时,这个样式文件就已经注定是要被翻来覆去的重构的了,甚至,每个来维护这个文件的人都会将其重构一遍。
**
- BEM保证样式不冲突的核心就是:在元素类名中加入唯一的标识。这个标识在BEM中对应的是模块名,也可能是一个独一无二的乱序字符串。为模块中每个元素名加入标识,这可是重复的工作啊,重复的工作就应该交给机器去做。webpack加载器css-loader,可在js中读取css样式,自2015年4月份起,该插件加入了placeholder功能,使得该插件可以解决CSS作用域的问题,原理也就是给元素的名称加入唯一的标识。
我原来存在的问题
- 滥用子代选择器和样式权重
在没有了解BEM命名方法时,我是如下命名。
<!-- Start Search Bar 模块 -->
<div class="search">
<input class="input">
<button class="btn">submit</button>
</div>
<!-- End Search Bar模块 -->
因为早就发现了CSS代码写完以后特别乱,而还存在样式优先级,经常导致样式被覆盖,于是我就层层嵌套(诸如 .search .input
,因为我知道样式越具体优先级越高,样式就不会被覆盖了,层层嵌套一是为了避免样式被覆盖,二是为了提高模块组件化(但是实际上我并没有做到,当.input
元素需要修改时,我需要去样式表里全局搜索,这时会出现多个同名的,它们属于不同的模块,.search .input
这样写好处是提高了样式表的可读性,从语法上我知道 input 是 search 的子元素),这样的开发效率简直太低下了,而且页面复杂的话,嵌套层级会非常高,耦合度高,一旦发生修改就很麻烦。),没有使用 Block-Element
是因为觉得类名太冗长了,其实不长。后面也有搭配 CSS的预处理器 SCSS 组织样式代码。
使用BEM命名以后
<!-- S Search Bar 模块 -->
<div class="search-bar">
<input class="search-form__input"/>
<button class="search-form__button"></button>
</div>
<!-- E Search Bar 模块 -->
以上内容部分参考:CSS命名规范-BEM
实现元素居中的几种方式
- 水平居中
- 垂直居中
参考 CSS居中
响应式页面开发
响应式页面开发的能力可以定义为:
编写一套代码实现页面的布局和排版,可以适配不同分辨率的设备。
响应式页面开发要求我们解决两大问题:
- 为不同特性(如横屏还是竖屏等)的浏览器视窗使用不同的样式代码
- 让页面元素的尺寸能够依据浏览器视窗尺寸变化而平滑变化。
flex 布局
参考 flex 布局 。
viewport meta 标签
<meta name="viewport" content="width=device-width, initial-scale=1">
viewport 是 “视口”的意思,也就是网页。上面代码的意思是将网页宽度(width)设置为设备宽度(device-width),网页初始缩放比例为1,也就是网页初始大小占屏幕面积的100%。
CSS 单位都有哪些?
响应式页面开发关键步骤
一、向 head 标签内添加 viewport meta 标签
二、viewport 单位 + rem + 媒体查询,代替直接使用 像素 px 单位
在得出移动端响应式布局之前,还做了一些尝试。使用 Media Queries 解决了「为不同特性的浏览器视窗使用了不同的样式代码」。使用 viewport 单位 和 rem 解决了「让页面元素的尺寸能够依据浏览器视窗尺寸变化而平滑变化」。
- 仅仅使用媒体查询实现响应式布局不足在于编写多套样式代码,包括不同分辨率设备的样式代码和样式断点处的特定代码,而且样式过渡会出现明显的断层。工作量大,用户体验也不好,肯定不推荐。
Media Queries 是为指定特性的浏览器视窗应用指定样式的手段,可以看成是 CSS 样式的过滤器或拦截器,通常情况下它可以通过 「@media 规则」结合「6 个查询参数」来拦截设备的浏览器特性(如显示类型、视窗高度、视窗宽度、横竖屏等),藉此可以为不同的特性应用不同的样式代码(相当于为不同的设备应用了不同的 CSS 样式)。
还需要设置样式断点。Media Queries 所使用的查询参数的临界值又可称为「样式断点」。
使用桌面版的 Chrome 浏览器,打开 Google 的 在线 Media Queries 例子 直观感受下使用 Media Queries 的效果(请注意缩放浏览器窗口观察页面展示效果),特别注意样式断点处的效果。
- 仅使用 rem 实现响应式布局,首先通过 JS 代码监听设备的分辨率来动态修改根元素字体大小,然后用 rem 单位取代页面中所有的 px 单位。这种方式导致 JS代码和CSS样式代码耦合。不推荐哦
- 仅仅使用 vw 作为CSS的唯一单位。可以实现常见的响应式布局,用户体验可以进一步优化。实际开发也不推荐。
首先编写 Sass 函数 vw 将设计稿元素尺寸的像素单位转换为 vw 单位,然后使用「vw 单位」取代页面中所有「px 单位」。
/* 通常移动端开发时,设计师以iPhone6 手机尺寸为基准提供设计稿 */
$vw_design = 750 /* 这是设计稿宽度,也就是iPhone6手机屏幕宽度,物理像素 */
@function vw($px) {
/* $px 是设计稿标出的尺寸,物理像素 */
@return $px / $vm_design * 100vw
}
/* 应用 */
.mod-nav {
&_item {
font-size: vw(24);
padding: vw(20) vw(10) 0;
}
}
1 物理像素线(也就是普通屏幕下 1px ,高清屏幕下 0.5px 的情况)采用 transform 属性 scale 实现
.mod_grid {
position: relative;
&::after {
/* 实现1物理像素的下边框线 */
content: '';
position: absolute;
z-index: 1;
pointer-events: none;
background-color: #ddd;
height: 1px;
left: 0;
right: 0;
top: 0;
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
-webkit-transform: scaleY(0.5);
-webkit-transform-origin: 50% 0%;
}
}
...
}
对于需要保持高宽比的图,应改用 padding-top 实现。
.mod_banner {
position: relative;
// 使用padding-top 实现宽高比为 100:750 的图片区域
padding-top: percentage(100/750);
height: 0;
overflow: hidden;
img {
width: 100%;
height: auto;
position: absolute;
left: 0;
top: 0;
}
}
由此,我们不需要增加其他任何额外的脚本代码就能够轻易实现一个常见布局的响应式页面,效果如下:
体验地址:视口单位布局 —— vw 单位
友情提醒:桌面版 Chrome 支持的字体大小默认不能小于 12PX,可通过 「chrome://settings/ 显示高级设置-网络内容-自定义字体-最小字号(滑到最小)」设置后再到模拟器里体验 DEMO。
仅使用 vw 作为CSS的唯一单位实现的响应式布局看起来很好,不足在于由于全部使用 viewport 单位,页面布局完全跟随视口变化而自动缩放,当视口过大或者过小,页面的布局也过大或过小,失去了最大最小宽度限制,从而导致在视口过大过小时,页面的排版和布局效果并不好,出现字体和元素过大或者过小等问题。
- 最优移动端响应式页面布局:vw + rem + Media Queries 。最优解推荐推荐
三个技术搭配使用,关键在于:
- 给根元素的字体大小设置随着视口变化而变化的 vw 单位,这样就可以实现动态改变根元素字体大小
- 通过 Media Queries 限制根元素字体大小的最大最小值,配合 body 加上最大宽度和最小宽度,实现布局宽度的最大最小限制
- 其他元素的文本字号大小、布局高宽、间距、留白都使用 rem 单位
第一点中的“动态”是指切换不同分辨率的设备,或者用户手动缩放浏览器,因为我们通过 viewport meta 设置了视口的宽度=设备宽度,因此改变设备的宽度就是在改变视口的宽度,而根元素使用了 视口单位 vw ,从而根元素字体大小也被改变了,这样的好处是不需要开发人员编写JS脚本动态改变根元素的字体大小了,编写低耦合的代码。
/* rem 单位换算:定为 75px 只是方便运算,750px-75px、640-64px、1080px-108px,如此类推 */
$root_fontsize = 75 /* 设置根元素大小基准75px,设计稿尺寸标注尺,使用物理像素 */
$vw_rem_design = 750 /* 设计稿基准尺寸,使用物理像素 */
/*
* @param {$px} 设计稿标注的尺寸,物理像素
*/
/* rem 函数将 px 转为 rem 单位 */
@function rem($px) {
@return $px / $root_fontsize * 1rem
}
/* vw 函数将 px 转为 vw 单位 */
@function vw($px) {
@return $px / $vw_rem_design * 100vw
}
html {
font-size: vw($root_fontsize); /* 1.根元素字体大小使用 vw 单位 */
/* 2.使用媒体查询限制根元素字体的最大最小值 */
@media screen and (min-width: 320px) {
font-size: 32px;
}
@media screen and (max-width: 540px) {
font-size: 54px;
}
}
/* 2.限制 body 元素的最大宽度和最小宽度 */
body {
max-width: 540px;
min-width: 320px;
}
/* 3.应用 */
.mod-nav {
&_item {
font-size: rem(24);
padding: rem(20) rem(10) 0;
}
}
在实际开发中,为了可以直接使用设计稿标注尺寸而不用再进行二次转换,设置根元素字体大小基准
$root_fontsize
和设计稿基准$vw_rem_design
要么都用物理像素,要么都用逻辑像素,具体使用哪个,以设计稿为准,比如我司设计师给的设计稿(如 墨刀)标注的是物理像素,基准都设置为物理像素,在开发时,向 rem 和 vw 传入的就是设计稿标注的尺寸数值,无需二次转换。为了方便计算,根元素大小 数值 通常设置为 设计稿宽度 数值 的 1/10 。 注意,根元素字体最大最小值限制和 body 宽度限制使用的是逻辑像素。
结合上面代码对第二点进行说明,首先明确 body 里面才是我们展示信息的地方,现在很多网站都使用视口中间是内容区,两边留白的布局就是因为限制了body 元素的最大宽度和最小宽度。min-width: 320px;
意思是当设备宽度小于320px (原因是用户缩小浏览器或者设备本身宽度小)时,body 就固定宽度为320px,也就是网页的内容区布局占320px宽,此时因为视口宽度不够320px,就出现横向滚动条。max-width: 540px
意思是当设备宽度大于 540px (原因是用户放大浏览器或者设备本身宽度大)时,body 就固定宽度为540px,也就是网页的内容区布局占540px宽,此时虽视口宽度变大,但网页布局不会改变,此时就出现两侧留白。当 320px < 设备宽度 < 540px
时,网页布局才是响应式的,在这个区间缩放视口,页面的排版和布局会自动缩放。可以通过坐标轴来理解,如下图:
vm + rem + media + 宽度限制 实现的布局是响应式的,它集单独使用一种技术实现布局的优点于一身。点击体验
现在我们来对比一下 「视口单位布局」和 「vm + rem + media+ 宽度限制 布局」的效果。
- 当 320px < 设备宽度(也是视口宽度) < 540px,两者实现的响应式布局效果一样,就不贴图了。
- 当 320px < 设备宽度(也是视口宽度),贴上对比图,一眼就看懂了。
上面是 纯视口单位布局。底部不会出现导航条,元素尺寸和字体大小太小。
上图是 限制宽度的 vw + rem + media 布局。底部会出现导航条,元素尺寸和字体大小合适。
- 当设备宽度(也是视口宽度) < 540px,贴上对比图,一眼就看懂了。
上图是 纯视口单位 布局。
上图是 限制宽度的 vw + rem + media 布局。
通过对比可知,后面才是我们想要的移动端响应式页面布局。
响应式页面开发参考凹凸实验室 《利用视口单位实现适配布局》,太感谢了,多年的疑问终于得到解答。