美文网首页CSS权威指南
Css权威指南(4th,第四版中文翻译)-7.基本的Visual

Css权威指南(4th,第四版中文翻译)-7.基本的Visual

作者: 秋名山车神12138 | 来源:发表于2018-10-30 18:09 被阅读0次

    这一章主要讲解下CSS在渲染的时候的理论原理。没人或书籍能够覆盖到css的所有细节,所以在我们日常使用中遇到css的问题的时候,了解CSS的渲染逻辑将有效帮助大家排查问题。


    基本的盒模型

    CSS的基本思想就是假定每个元素都是由1个或多个盒子组成的,称为element boxes。在每个box的中间有一块内容区域(content area),而包围内容区域的就是像padding, border, outline和margin的各种可选边框,之所以说是可选的因为所有这些边框的默认宽度都是0,如下图所示:


    7-1.png

    像padding,border和margin都有各自的设置选项,但outline就没有了。另外内容的背景也只应用与padding内部,margins都设置为透明的,这样可以让所有父元素的背景都看的见。padding是不准有负数的,但margin却可以,在后面咱们会详细讨论。

    我们先从border开始,border类型包括solid和inset(inset就是内嵌的意思,看起来效果像凹槽)。border的颜色通过border-color设置,如果没有被设置的话,那就默认采用前景色(foreground color,不清楚么,如果说元素内容的颜色是不是清楚了),最后要记住border的宽度是不准设置负值的。

    快览

    首先我们来解释下后面要用到的几个重要的概念:

    1. Normal flow:正常的流
      这个指的是文本默认的渲染顺序,一般语言都是一样的:从左到右,从上到下。而在某些特殊的语言中会有所不同,而咱们HTML语言其实也是遵循这一规则的,既然前面说过每个元素都是box,所以正常的流的意思就是,元素的box默认以这种顺序排列。那怎么样才能跳出这种normal flow呢?方法也有很多,可以设置为float, position, 或是flexible box 和grid layout。

    2. Nonreplaced element: 无法替代的元素
      这主要指那种内容放在document的元素,举个例子,<p>就是一个无法替代的元素,因为其文本内容就在元素自己当中。

    3. Replaced element: 可替代元素
      和上面相反,这里的元素是可以替换的,例如img元素,他其实就是指向一张图片;同样,大部分的表单元素也都是可替换的。

    4. Root element: 根元素
      这个我们很熟悉了,就是document文档树的根节点,像咱们常用HTML文档,其根节点就是html;而在XML文档中,根节点可以任意指定,例如RSS文件的根节点就是rss。

    5. Block box
      block的盒子它就像积木一样是纵向堆叠的,常用的有段落p,标题h1~6,和最常用的div。想要生成block盒子的元素可以通过声明display:block来实现。

    6. Inline box
      跟block相对的就是inline box, 常见的inline类型有strong和span等。如果想要变成inline box 只要声明display:inline就好。

    7. Inline-block box
      这个box在内部表现的像block,而在外部表现的像inline。就是有点像可替换元素。

    除了以上的这几种盒子,其实还有像table-ce的其他盒子,由于各种原因就不一一细说了。

    containing block

    对于CSS的盒子来说,父元素的box是很重要的,类似于编程的继承,子元素也会继承父元素的框架上下文(layout context)。在CSS中定义了一系列的规则来确定盒子的containing block。

    来看个具体的例子:

    <body> 
      <div>
        <p>This is a paragraph.</p> 
      </div>
    </body>
    

    p的containing box就是div,而div的就是body。而这个body因为是第一个元素被称为初始的containing box,之所以这么特殊,单独命名因为视口viewport决定了该元素的布局大小,而不是由根元素的内容大小决定的。


    调整element显示display

    在css中应该是最重要的一个属性了:


    t7-1.png t7-2.png

    改变默认的显示方式

    <nav>
    <a href="index.html">WidgetCo Home</a>
    <a href="products.html">Products</a>
    <a href="services.html">Services</a>
    <a href="fun.html">Widgety Fun!</a>
    <a href="support.html">Support</a>
    <a href="about.html" id="current">About Us</a> <a href="contact.html">Contact</a>
    </nav>
    

    nav默认谁inline的布局,如果我们想要改变他的显示方式,可以这样设置:

    nav a {display: block;}
    

    这会使所有的a元素都以block的元素来呈现。


    t7-2.png

    下面再考虑另一种情况,就是把block的元素改变从inline的

    <ul id="rollcall"> 
    <li>Bob C.</li>
    <li>Marcio G.</li> 
    <li>Eric M.</li> 
    <li>Kat M.</li> 
    <li>Tristan N.</li> 
    <li>Arun R.</li>
     <li>Doron R.</li> 
    <li>Susie W.</li>
    </ul>
    
    #rollcall li {display: inline; border-right: 1px solid; padding: 0 0.33em;} 
    #rollcall li:first-child {border-left: 1px solid;}
    
    7-3.png

    虽然大部分时候都可以这样来操作,但是h5本身是有限制的,即block元素包含inline元素可以,但是inline元素中包含block是不允许的,尤其是对于某些默认的inline和block元素,来看个例子:

    <span style="display: block;">
      <p style="display: inline;">this is wrong!</p> 
    </span>
    
    

    上面的强行转换其实是没用的,display只能改变元素的展示,而不乏改变元素的类型,所以有些跟类型强绑定的属性是无法通过display来修改的。
    接下面我们就来看看不同的box:包括block box, inline box, inline-block box 和list-item box。

    Block box

    block 盒子其实用的是最多的布局元素,但有时候其显示会超出你的预期,这就需要对该盒子的某些边界有清楚的了解,来看看下面这张图:


    7-4.png

    默认来说,block box的宽度设置的是left inner edge 到right inner edge的距离,height同理。

    不过我们可以通过设置box-sizing属性来修改这一规则:


    t7-3.png

    这个属性就是用来设置widht和height的定义规则的,默认的上面说了,如果设置为border-box,那么其宽度的界定将基于左右border边界的距离,同理可知padding-box的含义。


    7-5.png

    box-sizing水平的变化

    通过改变box-sizing会导致水平方向计算的变化,来看个例子:

    <p style="width: 200px; padding: 10px; margin: 20px;">wideness?</p>
    
    7-6.png

    水平方向的属性

    共有7个属性决定水平的长度:margin-left, border-left, padding-left, width, padding-right, border-right 和 margin-right。具体参照下图:


    7-9.png

    之前有提到过的一个containing block的概念,其宽度就是这7中长度的和。而在这7个值里面,只有三个是默认设置为auto的,那三个呢?左右的margin和width,其余的值都需要设置数值,有些的默认值为0.


    7-10.png

    而width只能出现2中情况,auto和非负的数值。

    使用auto

    auto的意思就是自动调整,调整的目标就是子元素的整个长度等于父元素的宽度。

    那有人就问啦,如果三个auto都设置了具体值,那怎么计算呢?这在CSS中有个专有名词overcontrained,意思就是限制过多了,那么css就用将margin-right强制设置为auto。

    div {width: 500px;}
    p {margin-left: 100px; margin-right: 100px;
    width: 100px;} /* right margin forced to be 300px */
    
    7-11.png

    如果左右margin都设置了,但width为auto,那么width就承担起自动填充的任务:

    p {margin-left: 100px; margin-right: 100px; width: auto;}
    

    多个auto的情况

    比如margin都设置为auto,那么左右都会设置相同的长度,来看个例子:

    div {width: 500px;}
    p {width: 300px; margin-left: auto; margin-right: auto;}
            /* each margin is 100 pixels wide, because (500-300)/2 = 100 */
    
    7-13.png

    所以很多想要居中的内容都是讲margin设置为auto来实现的。

    最后一种情况就是三个都设置为auto,那么margin就会都被设置为0,width会尽量填充父元素宽度。

    负的margin

    来看个例子:

    div {width: 500px; border: 3px solid black;}
    p.wide {margin-left: 10px; width: auto; margin-right: -50px; }
    
    7-16.png

    看起来已经超出了父元素的范围,但在数学上还是对的:

    10px+0+0+540px+0+0−50px = 500px

    还记得之前说的auto么,在这里,我们同样可以通过设置auto来抵消子元素的边界超出问题:

    div {width: 500px; border: 3px solid black;}
    p.wide {margin-left: 10px; width: 600px; margin-right: auto;
    border: 3px solid gray;}
    

    对应的公式如下:

    10px+3px+0+600px+0+3px−116px = 500px

    记住padding, border 和width是不能有负数的。

    百分比

    有时候百分比还是很有用的,尤其是在动态确定宽度比例的情况下:

    <p style="width: 67%; padding-right: 5%; padding-left: 5%; margin-right: auto; margin-left: 5%;">playing percentages</p>
    

    不过如果要混合使用百分比和具体数值的话会让人很困惑:

    <p style="width: 67%; padding-right: 2em; padding-left: 2em; margin-right: auto; margin-left: 5em;">mixed lengths</p>
    

    值的注意的是border是不接受百分比值的。

    可替换元素

    对于图片元素这种可替换元素来说,如果width指定为auto,那么元素的width将会是元素内容的实际宽度:

    <img src="smile.svg" style="display: block; width: auto; margin: 0;">
    

    当然我们应该设置图片的实际宽度:

    <img src="smile.svg" style="display: block; width: 25px; margin: 0;"> 
    <img src="smile.svg" style="display: block; width: 50px; margin: 0;"> 
    <img src="smile.svg" style="display: block; width: 100px; margin: 0;">
    
    
    7-18.png

    垂直方向的格式化

    刚说完水平的,下来来看看垂直高度的几个有趣的地方。首先是一个元素的内容决定了元素的默认高度,同时内容的宽度也会影响高度。那就会出现两个问题,自己设置的高度高于内容高度,自己设置的高度低于内容高度。对于前者,会保留正常的空白;而对于后者,则依赖于另一个属性overflow:


    7-19.png

    垂直方向属性

    对照水平方向,也是有7个属性:margin-top, border-top, padding-top, height, padding-bottom, border-bottom, and margin-bottom.


    7-20.png

    跟水平一样,垂直方向的auto也是这三个:


    7-21.png

    auto的触发规则与水平方向是一样的。

    百分比高度

    <div style="height: 6em;">
    <p style="height: 50%;">Half as tall</p>
    </div>
    

    其中p的高度就是3em。由于top和bottom的margin如果设置为auto都会被强制设置为0,因此想要垂直居中的方式就是各设置为25%。但万一父元素的高度没有设置,那么这个百分比高度就会被重置为auto:

    <div style="height: auto;">
    <p style="height: 50%;">NOT half as tall; height reset to auto</p>
    </div>
    
    
    7-22.png

    垂直方向margin的合并

    合并(可能翻译不太准确,英文为collapsing), 只会发生在margin中,而不会在padding和border中。
    举个例子:

    li {margin-top: 10px; margin-bottom: 15px;}
    

    在一串li中设置每个的上下margin间距,一般是不是都会以为每个li的间距应该是25px,而实际上结果却是15px。这是因为发生了合并,在两个相邻的margin中会选取其中大的那个。


    7-24.png

    再来看个例子:

    ul {margin-bottom: 15px;}
    li {margin-top: 10px; margin-bottom: 20px;} 
    h1 {margin-top: 28px;}
    

    从上面可知,这次li和h1的实际间隔其实是28px


    7-25.png

    负的margin和合并collapsing

    7-27.png

    来看另一个例子:

    p.neg {margin-top: -50px; margin-right: 10px; margin-left: 10px; margin-bottom: 0; border: 3px solid gray;}
    
    <div style="width: 420px; background-color: silver; padding: 10px; margin-top: 50px; border: 1px solid;">
    <p class="neg"> A paragraph.
    </p>
    A div.
    </div>
    
    7-28.png

    再来看个margin为负的例子:

    p.neg {margin-bottom: -50px; margin-right: 10px; margin-left: 10px; margin-top: 0;
    border: 3px solid gray;}
    
    <div style="width: 420px; margin-top: 50px;"> <p class="neg">
    A paragraph.
    </p> </div>
    <p>
    </p>
    
    7-29.png

    List Item

    我们常用的list item本身其实是有不少特殊的显示规则的,比如marker,圆点和数字,而且这些都不在内容区域中,所以上面所说的一系列css规则在他们身上也是通用的:


    7-31.png

    内联元素

    介绍完了block元素就可以来看看inline元素了,常见的有em和a标签,这两个都是不可替代的,而像image则是可替代的元素。注意我们后面讨论的内容不适用于table元素,在CSS2中引入了很多新的属性和行为来出来表单元素的显示,而这远远超过了本书的范围。

    可替换和不可替换的元素在inline模式下CSS的处理是有所区别的。

    Line Layout 线框

    首先我们要确定的就是inline的内容是怎么放的,因为这不像block元素那么简单的创建个block 盒子,而且其他元素是不能共存在同一块区域的。那人们自然会很好奇,inline元素在block元素中是如何展示及修改样式的。

    为了了解线是怎么生成的,首先来看下单行长文本的情况,如下图所示:

    span {border: 1px dashed black;}
    
    7-33.png

    我们给span元素设置了一个简单的虚线边框。

    基本概念

    在我们进一步深入之前,让我们来回顾下线框中用到的基本概念:

    • 匿名文本:
      这个主要指的是没有包含在inline元素里面的内容,比如
    <p> I'm <em>so</em> happy!</p>
    

    其中的i'm happy!就是匿名文本,注意空格也算是字符。

    • Em box
      这是在给定字体中定义的,或者称为字符box。在CSS中期em box的高度由font-size来定义。

    • leading
      leading其实是font-size和line-height的差。一般来说,这个差值将会一分为二,均匀分布在内容区域的top和bottom部分。leading只会应用在不可替换的元素上面。

    inline 不可替换元素

    接下来我们就来一步步 看下inline元素的创建过程,首先引入的是font-size的大小来决定内容区域的高度。如果一个元素的font-size为15px的话,那么内容区域的高度就是15px,如下图所示:

    7-36.png

    接下来需要考虑的就是元素的行高,同时注意与font-size的高度差。如果元素的font-size=15px而line-height=21px那么这个差就是6px,接下来浏览器会将这6px均分到上下两部分,如下图所示:


    7-37.png

    让我们假定下面是没问题的:

    <p style="font-size: 12px; line-height: 12px;">
    This is text, <em>some of which is emphasized</em>, plus other text<br> which is <strong style="font-size: 24px;">strongly emphasized</strong> and which is<br>
    larger than the surrounding text.
    </p>
    

    可以看到,大多数文本的字体大小为12px,而strong的行高为24px,但由于行高是继承的,所以strong继承的行高为12px。对于strong来说line-height与font-size的差为-12px,所以上下都有-6px的分配,所以strong所代表的inline box是要小于内容区域的,最后的效果如下图所示。


    7-38.png 7-39.png

    从上图看出,中间那行会比其他两行高,但却没有完全包裹全部的文本内容。

    垂直对齐

    如果我们改变inline box的垂直对齐方式,高度计算的原理还是一样的:

    <p style="font-size: 12px; line-height: 12px;">
    This is text, <em>some of which is emphasized</em>, plus other text<br> which is <strong style="font-size: 24px; vertical-align: 4px;">strongly emphasized</strong> and that is<br>
    larger than the surrounding text.
    </p>
    

    上面的变化同时抬升了storng元素的内容区域和inline box,因为strong元素决定这inline box的上边界,因此这一改变会同时抬升inline box的上边界:


    7-40.png

    让我们考虑另一种情况:

    <p style="font-size: 12px; line-height: 12px;">
    This is text, <em>some of which is emphasized</em>,<br>
    plus other text that is <strong style="font-size: 24px;">strong</strong>
    and <span style="vertical-align: top;">tall</span> and is<br> larger than the surrounding text.
    </p>
    
    7-41.png

    虽然样式跟之前是一样的,但又没有发现tall文本的高度对齐到了inline box的上边界。因为tall的文本具有相同的fontsize和line-height。
    再来看下:

    <p style="font-size: 12px; line-height: 12px;">
    This is text, <em>some of which is emphasized</em>,<br>
    plus other text that is <strong style="font-size: 24px;">strong</strong>
    and <span style="vertical-align: top; line-height: 2px;">tall</span> and is<br> larger than the surrounding text.
    </p>
    

    由于tall文本的行高小鱼font-size,tall文本的inline box小鱼内容其余,这一小小的改变改变了tall文本的高度:


    7-42.png

    再来看看设置tall行高高于font-size的情况:

    <p style="font-size: 12px; line-height: 12px;">
    This is text, <em>some of which is emphasized</em>, plus other text<br> that is <strong style="font-size: 24px;">strong</strong>
    and <span style="vertical-align: top; line-height: 18px;">tall</span> and that is<br>
    larger than the surrounding text.
    </p>
    
    

    最后结合本章内容总结下vertical-align的关键词:

    • top:对齐元素inline box的上边界
    • bottom: 对齐元素inline box的下边界
    • text-top: 对齐父元素文本上边界
    • text-bottom: 对齐父元素文本下边界
    • middle: 对齐元素inline box的垂直中点
    • super: 上移内容区域和inline box,具体依赖浏览器实现
    • sub:跟super一样,下移内容
    • <百分比>:基于line-height的百分比来移动

    管理行高

    为了防止重叠,我们建议使用em单位:

    p {line-height: 1em;}
    big {font-size: 250%; line-height: 1em;}
    
    
    <p>
    Not only does this paragraph have "normal" text, but it also<br> contains a line in which <big>some big text</big> is found.<br> This large text helps illustrate our point.
    </p>
    

    通过设置big的行高,我们增大了对应inline box,而且不用改变所有行的行高。同时1em正好对应字体的大小:


    7-43.png

    设置行高的比例因子

    最好的设置行高的方式就是设置纯数字,可以被所有的元素所继承:

    body {line-height: 1.5;}
    
    p {font-size: 15px; line-height: 1.5;} small {font-size: 66%;}
    big {font-size: 200%;}
    
    <p>This paragraph has a line-height of 1.5 times its font-size. In addition, any elements within it <small>such as this small element</small> also haveline-heights 1.5 times their font-size...and that includes <big>this big element right here</big>. By using a scaling factor, line-heights scale to match the font-size of any element.</p>
    
    p {font-size: 15px; line-height: 1.5;} small {font-size: 66%;}
    big {font-size: 200%; line-height: 1em;}
    
    7-45.png

    添加box 属性

    想看看为inline 元素添加border的效果么:


    7-46.png

    inline元素的border属性其实是由font-size控制的,而不是line-height。 换句话说,如果一个span元素的font-size为12px然后行高为36px,那么其实际的内容区域的高度还是12px。但是border还是会包裹在inline元素周围的.
    接下来来看看padding属性,padding的添加并不会改变内容的高度

    span {padding: 4px;}
    
    7-47.png

    改变breaking 行为

    在上一节,我们看到了一个inline不可替代元素跨行显示的例子,它本身其实是单行的文本,每个字符被单独的box包围,行末被单独分割。这种默认的行为可以通过box-decoration-break来修改:


    t7-4.png

    就两个值:slice和clone。slice就是之前的默认值。而对于clone,其主要特定为将每个片段都作为独立得box,那这个独立是什么意思呢?来看个例子:


    7-50.png

    发现在下方的clone选项里面,背景图案不依赖文本独立克隆了背景信息。box-decoration-break属性虽然大多用在inline box中,不过其实它适用于任何的存在break的元素中。

    inline 可替换元素

    说到可替换元素,直接想到的就是img了。如下图所示:


    7-51.png
    p {font-size: 15px; line-height: 18px;}
    img {height: 30px; margin: 0; padding: 0; border: none;}
    

    可替换元素最大的特点就是就有自己的固有高度和宽度,而这是会影响布局的,在inline 元素中嵌入img,那么整行的line box就会被img的高度给撑开(假如img固有高度大于line-height)。上图中,由于图片没有margin,padding,border,因此inline box的高度就等于img的content 高度,也就是30px。

    p {font-size: 15px; line-height: 18px;} 
    img {vertical-align: 50%;}
    
    <p>the image in this sentence <img src="test.gif" alt="test image"> will be raised 9 pixels.</p>
    

    替代元素和baseline

    从上面的讨论中可以明白inline的可替换元素默认情况下是沿着baseline来排布的。如果元素有底部的padding,margin或是border的话,那么内容区域也会相应的抬升。而由于替代元素自己是没有baseline的,所以对齐的就是inline box的baseline:

    7-54.png

    inline-block 元素

    inline-block是在CSS2.1引入的聚合属性,融合了inline和block。那这个inline-block到底是什么意思呢?其核心就表现为对外显示inline属性,就像image一样,而对内则表现出block属性,可以设置相应的width和height属性。让我们来看个具体的例子:

    <div id="one">
    This text is the content of a block-level level element. Within this block-level element is another block-level element. <p>Look, it's a block-level paragraph.</p> Here's the rest of the DIV, which is still block-level.
    </div>
    <div id="two">
    This text is the content of a block-level level element. Within this block-level element is an inline element. <p>Look, it's an inline paragraph.</p> Here's the rest of the DIV, which is still block-level.
    </div>
    <div id="three">
    This text is the content of a block-level level element. Within this block-level element is an inline-block element. <p>Look, it's an inline-block paragraph.</p> Here's the rest of the DIV, which is still block-level.
    </div>
    

    然后在上面的三个div分别应用下面的样式:

    div {margin: 1em 0; border: 1px solid;}
    p {border: 1px dotted;}
    div#one p {display: block; width: 6em; text-align: center;}
    div#two p {display: inline; width: 6em; text-align: center;} div#three p {display: inline-block; width: 6em; text-align: center;}
    
    7-56.png

    再举个具体的使用例子,比如在导航栏中有5个链接,一边想要他们inline显示,而同时要设置他们的宽度为父元素的20%,name就可以使用inline-block来设置:

    nav a {display: inline-block; width: 20%;}
    

    Flow 显示

    flow和flow-root的属性还是值得一说的。其实CSS的显示分为两个类型:外部类型和内部类型。像之前说的inline,block都是控制外部类型显示的,而flow就表示内部元素的排列方式。
    display:flow默认会同时绑定block和inline的外部框架,如果没有定义inline,name就以block形式展示:

    #first {display: flow;} 
    #second {display: block flow;} 
    #third {display: inline flow;}
    

    上面的例子中first和second显示结果为block,而third则表现为inline box。
    所以一般在display后面的参数中,第一个表示外部类型,而第二个表示内部类型。比如:display: inline table。含义就是在外面以inline形式表示,而对内则生成一个table的上下文。

    说完了flow,下面看看flow-root,这个值和flow不一样,默认就是block类型的。这个值一般是针对root 元素的,一般就是html。

    最后来看下display 新老值得对应关系:

    老的值 新的值
    block block flow
    inline inline flow
    inline-block inline flow-root
    list-item list-item inline flow
    table block table
    inline-table inline table
    flex block flex
    inline-flex inline flex
    grid block grid
    inline-grid inline grid

    内容显示

    另一个新增的display值就是contents,这个值的用处试讲元素从页面格式中去除。来看个例子:

    ul {border: 1px solid red;} 
    li {border: 1px solid silver;}
    
    <ul>
    <li>The first list item.</li> 
    <li>List Item II: The Listening.</li>
     <li>List item the third.</li>
    </ul>
    

    上面的例子会渲染一个带红色白框的ul list,而如果我们在ul上加上display:contents,name浏览器在渲染的时候就会像忽略外框的ul一样,如图所示:


    7-58.png

    其他的display值

    在本章中我们其实还有很多display的值没有被覆盖到,大多数都是跟ruby相关的值,直到2018年底也没得到很好的支持。

    计算得出的值

    如果元素设置为float或是position,那么计算出的display值也可能发生变化。事实上,display,position和float这三个值是有制约关系的。
    如果一个元素的position设置为absolute,那么float就被自动设为none。而对于浮动和绝对定位的元素,其计算值由下表给出:


    t7-2.png

    小结

    虽然CSS的format模型在有些地方是反直觉的,但随着使用的深入会慢慢理解它们。block元素是比较好理解的,而inline元素相比较就会显得比较麻烦。

    相关文章

      网友评论

        本文标题:Css权威指南(4th,第四版中文翻译)-7.基本的Visual

        本文链接:https://www.haomeiwen.com/subject/mufntqtx.html