跳至主要內容

div、span和盒模式

xmut-lby大约 16 分钟

div、span和盒模式

div 标记

你注意到了吗?

页面上最常见的标记是什么?

打开任意一个页面(再问一下,怎么打开?),例如B站,你看到的最多的标记一般都是<div>

div div 还是 div
div div 还是 div

所以div是什么标记呢?

页面模块化

我们思考下有这样一个需求:

模块划分
模块划分

这个页面可以简单分为三个部分,每部分的颜色各不相同。如果要你实现这个页面,你要如何实现?

这里假设每个模块都有一个标题(<h1>)和一个段落(<p>)组成,那么html代码可以这么写:

<h1>html5</h1>
<p>html5 的内容</p>

<h1>bootstrap</h1>
<p>bootstrap 的内容</p>

<h1>vue</h1>
<p>vue 的内容</p>

这里显然需要三种不同的颜色,所以在css部分,需要设置三种颜色。这里比较常见的是为每一种颜色设置一个class

.h5 {
    color: red; /* 假装颜色是对的 */
}
.boostrap {
    color: purple; /* 假装颜色是对的 */
}
.vue{
    color: green; /* 假装颜色是对的 */
}

那么我们为对应的段落加上对应的段落:

<h1 class="h5">html5</h1>
<p class="h5">html5 的内容</p>

<h1 class="bootstrap">bootstrap</h1>
<p class="bootstrap">bootstrap 的内容</p>

<h1 class="vue">vue</h1>
<p class="vue">vue 的内容</p>

就可以实现了。

但这里有两个问题,当然实际上是一个问题:

  1. 你写了很多遍class='xxx',我们这里只是一个简单的例子,实际上一个模块可能存在非常多的标记,一个个去写或者修改显然非常繁琐。
  2. 从逻辑上说,我们知道这些块是一个整体,但在代码上并没有体现这点。对代码阅读和理解来说并不友好。

这两点归根到底是一句话:模块化程度太低。

用div规划页面

div标记就可以用来规划页面,div实质上是一个空的块元素标记,他的作用就是把其他的标记放进去,方便我们对页面进行逻辑规划。

对于上面的例子,我们可以这样做:

<div class="h5">
    <h1>html5</h1>
    <p>html5 的内容</p>
</div>

<div class="bootstrap">
    <h1>bootstrap</h1>
    <p>bootstrap 的内容</p>
</div>

<div class="vue">
    <h1>vue</h1>
    <p>vue 的内容</p>
</div>

这样页面的模块化显然大大增强了。

显然div是可以嵌套的,比如我们可以这样配置:

<div class="header">
    .... <!-- 页头 -->
</div>
<div class="content">
    <!-- 页面内容 -->
    <div class="h5">
        <h1>html5</h1>
        <p>html5 的内容</p>
    </div>

    <div class="bootstrap">
        <h1>bootstrap</h1>
        <p>bootstrap 的内容</p>
    </div>

    <div class="vue">
        <h1>vue</h1>
        <p>vue 的内容</p>
    </div>
</div>
<div class="footer">
    .... <!-- 页脚 -->
</div>

我们可以用逻辑图来表示这个布局:

页面的布局形式
页面的布局形式

这样配置的好处也是显而易见,我们可以针对更具体的模块配置不同的css。

子代选择器

我们可以用>符号来表示后代从属关系。例如:

.h5>h1{ 
    /* .. 对h5 类下的 h1生效 */
}

会对这个标记生效:

    <div class="h5">
        <h1>html5</h1> <!-- h5 class 下的 h1-->
    </div>

同理也可以有如下选择器:

.content>.bootstrap

对下面的块生效:

<div class="content">
    <!-- ... -->
    <div class="bootstrap"> <!-- .content 下的 .bootstrap-->
        <h1>bootstrap</h1>
        <p>bootstrap 的内容</p>
    </div>
    <!-- ... -->
</div>

你可以推演出很多不同的规则。不过我要再次强调,刻意去背或者记没有太大的意义,最好的做法是当你用多了,你就记住了。这也说明为什么充足的代码量如此之重要。

常用的css风格

这里介绍几个可能会用得着的常用风格,实际上你经常会遇到。

字体风格

常用的有:font-familyfont-sizefont-weightfont-style这几项。分别对应字体,字体尺寸,加黑和斜体。

font-family

这个规则用来指定页面使用的字体,例如(点开“字体演示”左侧的按钮可以看到源码,右侧两个按钮可以进入互动测试的页面):

字体演示
<h1>This is a h1 tag</h1>
<h2>This is a h2 tag</h2>
h1{
    font-family: "Times New Roman", Georgia, Serif;
}
h2{
    font-family:Arial, Verdana, Sans-serif;
}

可以明显看出h1h2这两者的字体的不同。这里有几个点需要注意下:

  1. Times New Roman、Georgia和Serif这些都是字体的名称,你可以在控制面版的字体里看到本机上安装的字体。
  2. 字体选择的次序从前往后选,以h2为例,浏览器优先选择Arial,其次选Verdana最后选Sans-serif
  3. 有些字体名中间有空格,需要用引号"包裹起来,例如"Times New Roman"
  4. 注意,选择字体看的是浏览器所在机上的字体,而不是服务器上的字体。

最后一点有必要再详细解释一下,字体选择的逻辑是非常合理的。因为显示是浏览器的事,所以浏览器才是需要知道本机上安装了何种字体,并调用该字体进行渲染的一方。

由此可见这里指定多种字体是非常有必要的,因为html里指定的某个字体,可能在本机上并没有安装。比如这里的Times New RomanArial在某些Linux系统上可能是没有的。

为什么没有?

因为字体不是免费的,windows系统里的某些字体,例如微软雅黑,你可能以为是免费的,实际上是有版权的。微软购买了版权,所以你可以(在Windows系统上)使用。

然而你可以在Windows上使用,不代表你可以在你的商业产品上使用(比如,制作你的海报并卖给某些公司)。

实际上如果你在自己的商业产品上使用这些字体,是有可能面临相关的公司诉讼的,有不少人吃过这类亏了。

解决办法:使用免费开源字体(例如得意黑)。

所以这里正确的作法是应该将最优的显示字体往前放,这样目标机如果安装了此类字体,可以获得最好的显示效果。 然后把兼容性较好的字体往后放,保证你有一个保底。

自定义字体 font-face

如果你要使用自定义的字体,以保证字体最佳的显示效果,可以使用@font-face来定义自定义的字体:

我们以开源的得意黑open in new window的网页字体SmileySans-Oblique.otf.woff2作来演示。

将文件放入根目录下的fonts目录下:

文件布局
文件布局

在css里这样配置自定义的font-face:

@font-face {
    font-family: SmileySans; /* 自定义的字体名 */
    src: url(/fonts/SmileySans-Oblique.otf.woff2);
}

第一列的font-family指明了我们自定字体的名称,下面的url对应的是字体文件的url,这个url可以是本服务器上的,也可以是外链。 这样就注册了对应的字体名称,接下来我们就可以像普通字体一样使用他了:

h1{
    font-size: 400%;
    font-family: SmileySans; /* 引用自定义字体 */
}

在页面里嵌入一个h1标记,就可以看到效果。

<body>
    <h1>得意黑字体</h1>
</body>
自定义字体结果
自定义字体结果

font-size

上面的css配置里我们使用了font-size:400%。用来放大字体。

这个风格允许我们调整字体的大小。有三种计量方式:

  1. 直接指定字体尺寸(绝对尺寸),例如20px,30px,这里的px指的是pixel,即像素。
  2. 百分比,相对于父节点的比值,例如上例中的400%,就是相对body的字体尺寸的4倍。
  3. em,也是比例形式,但是以1为底,例如2em,就是2倍,等同于200%

一般来说我们应该首选相对比例,有助于我们进行等比例的页面缩放。

font-weight和font-style

分别对应加粗和斜体。font-weight常用的有normalbold这两个选项,分别对应常规(不加粗)和加粗。

此外你还可以用100~900的数字(字重)来表示加粗的程度。正常情况下normal对应400,bold对应700,不过要注意的是这是需要字体支持的,如果字体不支持对应的字重,会根据规则选择其中的一个。所以一般情况下我们只在normalbold之间切换。

font-style也一般是两个常用选项italicnormal,前者表示斜体,后者是正常。font-style还有一个选项是oblique。注意font-style也是需要字体支持的,如果字体不支持斜体,那么italic就不会生效。例如前面的得意黑,就不支持斜体(因为他本身就是斜的)。

oblique

font-style还有一个选项是oblique,这个属性可以用在某些不支持斜体的字体上,由浏览器来生成一个斜体效果。但这个的效果跟italic是不同的。你可以理解成一种斜体的模拟。

所以优先还是应该选择italic

为什么是italic

初学者常常会困惑为什么斜体叫italic,也就是意大利体,实际上西文字体是一个非常久远的话题。例如前述的Time New Roman,可以溯源到古罗马时代。不过大体上罗马体和意大利体都是文艺复兴时代的常用字体。

span 标记

如果说div是块元素的容器,那么span就是内联元素的容器。例如假如我们需要对内联元素中的一部分进行额外处理,我们可以用span把这部分包裹起来然后处理:

看下面的演示:

演示
<h1>Hello <span class="red">RED</span> <span class="green">GREEN</span> <span class="blue">BLUE</span><h1>
.red, .green, .blue{
    color: white;
}
.red{
    background-color: red;
}
.green{
    background-color: green;
}
.blue{
    background-color: blue;
}

这里可以看出,divspan实际上承担的不一样的职责,div负责把多个块元素组织成一个整体,而span则是负责从内联元素中切一块出来进行精细化控制。

也可以不严谨地说,div一般比普通块元素大,span比普通内联元素小。

盒模式

块元素和内联元素最大的区别在于,块有自己的“领地”,通常要占据从页面左侧到右面右侧的一整条空间(至少到目前是的)。

块的“领地”,占据一整行
块的“领地”,占据一整行

而内联元素的范围则是局部的:

内联元素没有自己的领地
内联元素没有自己的领地

我们把这个“领地”称为块元素的

盒的属性

浏览器调试模式右侧的样式和计算标签都可以显示块元素的盒的参数:

块元素的盒属性
块元素的盒属性

同样页面上的元素也会显示其范围。

块元素的盒从内到外一共有4层。第一层是块元素内容的尺寸,即这里的63 x 115.5,这块是块元素内容具体显示所占的空间(即你看到的得意黑字体这几个字)。

向外一层是padding,我们一般翻译成内边距,所谓边距都是空白空间,没有内容。再向外一层是border,也就是边框。再往外一层是margin,我们一般翻译成外边距。

盒模式的属性
盒模式的属性

这几个属性都可以单独设置。

width和height

如果不特别设置,块总是默认占据一整行。而高度会自动计算,以放下所有块的内容。

不过这两个属性都可以设置:

.minisize{
    width: 100px;
    height: 100px;
}

.ration-50{
    width: 50%;
}

当设置成百分比时,会计算为包含块(也就是父标记)对应属性的百分比。

有em吗?

有!这点要非常注意,实际上em的含义是font-size的取值,也就是说,如果你设置了3em,那么你的意思实际上是:字体高度的三倍。

见:https://developer.mozilla.org/zh-CN/docs/Web/CSS/lengthopen in new window

不同的width得到的结果如下:

不同width属性的结果,从上到下:不设置,200px和100px
不同width属性的结果,从上到下:不设置,200px和100px

试试

设置width:50px,然后拖动窗口改变窗口的宽度试试,跟固定的尺寸有什么区别?

你想过吗?

如果设置的height比所占据的空间大,会怎么样?

除此之外,还有min-widthmax-widthmin-heightmax-height四个属性,名称很清楚,不解释,如果不理解你可以(应该)自己试试效果。

边框border

边框用border-*族属性来控制:

  1. border-width控制边框的宽度。
  2. border-style控制边框的风格。
  3. border-color控制边框的颜色。
  4. border-radius控制边框圆角弧度。

请通过vscode的提示功能,尝试一下这几个属性的效果。

其中前三个属性是可以通过一行border进行设置的:

h1{
    border: 1px black thin;
}

其中第一项1px指的是border-width : 1px,第二项black指的是border-color : black,第三项thin指的是border-style : thin

之所以可以这样写,是因为这三项配置的值都是不同的,解析器可以正确解析出来对应的属性(所以,次序并不影响理解)。

::: tips 为什么不是整数? 有时候你会发现,border的数字不是整数,或者不是你设置的数字。

这是因为你的页面有缩放,注意你显示器的缩放倍率也会影响页面缩放倍率。 :::

由此可见,css其实是远比C语言灵活的语言。

内边距和外边距

内边距是内容和边框之间的空白间隔,外边距则是两个块之间的空白间隔。这两点比较清楚。

属性方向

大家应该能想到,不管内外边距还是边框,都是一个矩形,都有四个方向。

这四个方向是可以单独设置的:

h1{
    padding-top:    10px;
    padding-right:  20px;
    padding-bottom: 30px;
    padding-left:   40px;
}

在对应属性上加上方向的后缀,可以为某个具体的方向进行单独的设置。

对于border-*族,方向的后缀要放在border-后,再加具体的属性:

例如:

  • border-bottom-color:边框底边的颜色
  • border-top-width:边框顶边的宽度
  • border-left-style:边框左侧的风格
  • border-bottom-left-radius:左下角边框的弧度

当然也可以允许这样的设置:border-left : 1px thin solid,一次性为左侧边框设置widthstylesolid

简写法

对于marginpadding,也有简化的写法,依次在一行内写4个值,分别按上右下左的次序排列:

h1{
    padding: 10px 20px 30px 40px;
}

等同于:

h1{
    padding-top:    10px;
    padding-right:  20px;
    padding-bottom: 30px;
    padding-left:   40px;
}

次序很容易记,从上方开始顺时针排列。

也可以写两个值,分别指代上下和左右。

h1{
    padding 10px 20px;
}

等同于

h1{
    padding-top:    10px;
    padding-right:  20px;
    padding-bottom: 10px;
    padding-left:   20px;
}

怎么样,再次体会css语法的灵活性了吧。

外边距的合并

时间上外边距和内边距是有区别的。要特别注意的是外边距是相邻两个块共用的,相邻两个块的外边距会重叠。例如上面的块设置了外边距是20px,下面的块设置外边距10px,则两者之间的间隔并不会是10px+20px=30px,而是取两者的最大值20px

margin的合并
margin的合并

这样去理解比较容易:你家和邻居都建房子,你说我旁边的巷子要有2米,你邻居说我旁边的巷子要有1米。那么你们两家之间的巷子只要2米就够了。

这点要特别注意,因为所谓相邻,不仅包括平级的上下相邻,也包括包含关系的内外相邻

内外相邻的合并关系
内外相邻的合并关系

更离谱的是,会存在侵入父元素的现象:

内部元素的margin侵入到父元素
内部元素的margin侵入到父元素

极端的情况下,如果父元素没有margin,子元素的margin合并之后会凭空出现一个margin

父元素没有margin而子元素有的情况
父元素没有margin而子元素有的情况

还有一种是空标记(高度为0),会出现自己折叠自己的情况:

自我折叠
自我折叠

更多的内容可以看这里:外边距合并open in new window

text-align和text-decoration

text-align用来决定块内的内联元素的对齐方式,常用的有三种取值方式:leftrightcenter。默认取left

三种对齐方式,从上到下是left,right和center
三种对齐方式,从上到下是left,right和center

text-decoration可以为字加上装饰效果,具体可以看这里open in new window

margin : auto

margin有一个非常特殊的属性,auto,这个属性让浏览器自动选择边距,通常情况下会将块元素在他父节点里居中放置。

.margin-auto{
    width: 50%;
    margin: auto;
}

注意这个配置和text-align:center的区别。text-align:center块内内联元素的居中对齐。

margin:auto块元素在父节点里居中。这两者有何区别呢?

你应该注意到我们上面.margin-auto类里除了margin:auto之外,还配置了width:50%,这是必须的。因为如果没有配置width,块总是占满父块的整个宽度。 那这种情况下配置margin:auto就毫无意义了——占满宽度就已经是居中了嘛。

所以margin:auto的意思是:当我的宽度和父块宽度不一致时,将我放在父块的中间

可以对照下面的例子体会下:

不同的居中模式
<div>
    <h1 class="p1">only text-align:center</h1>
    <h1 class="p2">only margin:auto</h1>
    <h1 class="p3">margin:auto & width:50%</h2>
    <h1 class="p4">text-align:center、 margin:auto & width:50%</h2>
</div>
h1{
    border: 2px solid black;
}

.p1{
    text-align:center;
    margin: 5px 0px;
}

.p2{
    margin: 5px auto;
}

.p3{
    margin: 5px auto;
    width: 50%;
}

.p4{
    text-align:center;
    margin: 5px auto;
    width: 50%;
}

实际上现代浏览器上并不建议使用margin-auto,而是使用更为现代的flex布局方式:

display:flex
<div class="container">
    <h1 class="p1">现代的flex居中方式</h1>
</div>
.container{
    display : flex;
    justify-content: center;
}
.p1{
    width : 50%;
    border: 2px solid black;
}

这种方式现在更为推荐。

一个问题

background-colorbackground-image会影响哪些区域(内容、padding、border和margin里的哪些)?

想想看,要怎么验证你的想法。