CSS 理论进阶之布局

总结CSS常见布局

Posted by QY on March 15, 2019

前言

CSS作为表现层的重要组成部分,它的布局具有举足轻重的地位,为了开发出一个合格的网站界面,几种常见布局也是必备知识, 其中的一些概念基于CSS基本概念与使用中所讲的,因此对基本概念有困惑建议阅读基础内容

布局原理

  • 为了在实际的布局中理解其原理需要掌握以下知识点

CSS布局基本单位

元素类型与display的值决定了box的类型。不同的box会参与不同的Formatting context

  • box类型

    • block-level box

      display属性为block, list-item, table的元素, 会生成block-level box, 并且参与block formatting context

    • inline-level box

      display属性为inline, inline-block, inline-table的元素, 会生成inline-lebel box并且参与inline formatting context

Formatting context是页面中一块渲染区域, 并且有一套渲染规则, 决定子元素如何定位以及和其他元素的关系和相互作用

BFC只有block-level box参与, 规定了内部的block-level box如何布局, 并且与这个区域外部毫不相干

  • BFC布局规则

    1. 内部box会在垂直方向, 一个接一个放置
    2. BFC的区域不会与float box重叠
    3. 内部的box垂直方向距离由margin决定, 属于同一个BFC的两个相邻块级box的margin会发生重叠

      • 两个方式来使其不重叠, 其一是使两个元素属于不同BFC, 其二是使两个元素不相邻
    4. 计算BFC高度时, 浮动元素也参与计算
    5. BFC就是页面上的一个隔离的独立容器, 容器里面的子元素不会影响到外面的元素, 反之也是如此

出现BFC的情况

  1. 根元素(html)
  2. float属性不为none
  3. postionabsolutefixed
  4. overflow不为visible
  5. displayinline-block, table-cell, flex, inline-flex
haslayout

IE6,7特有的概念用以替代他们不具有的BFC特性

如果一个元素没有布局, 则它的尺寸和位置由最近拥有布局的祖先元素控制

IE团队是为了减少性能开销才将元素变成拥有布局和没有布局这两种情况

  • 默认拥有布局的元素

      html, body, table, tr, td, img, hr,
      input, select, textarea, button, 
      iframe, embed, object, applet, marquee
    
  • 触发haslayout的办法

    • IE6或IE7支持

      1. floatleftright
      2. displayinline-block
      3. positionabsolute
      4. width为除auto外的值
      5. height为除auto外的值
      6. zoom为除normal外的值
      7. writing-modetb-rl
    • IE7支持

      1. min-height:任意值
      2. min-width:任意值
      3. max-height: 除none
      4. max-width: 除none
      5. overflow, overflow-x, overflow-y: 除visible外任意值, 仅用于块元素
      6. position: fixed
垂直水平居中
  1. 已知高宽的水平居中

    • 将需要定位的元素设置为绝对定位
    • 使用绝对定位的元素自身规则使其垂直水平居中

      • 对于所有绝对定位元素它的几项属性一定满足以下两条要求:

        水平方向上: left + right + margin + padding + width = 包含块的宽度

        垂直方向上: top + bottom + margin + padding + height = 包含块的高度

      因此可以将left,top,bottom,right设置为0再设置固定高宽(padding默认值为0), 此时包含块剩余高宽会自动设置给margin

      为了让margin自动接收到正确的值需要将margin设置为auto, 最终便实现了垂直水平居中

      由于高宽默认值为auto, 因此未设置其高宽时默认会将绝对定位元素的高宽撑满父元素

  2. 未知高宽的水平居中

    • 设置绝对定位, lefttop设置为50%
    • 使用transform: translate3D(-50%, -50%, 0)

    兼容性较差, IE10及以上

line-height

行高定义为两行字的基线间距离, 常用于单行文本垂直居中

内容区:顶线到底线间的距离

行内框:内容区加上 (行高-字体大小)/2 的值

行框:一行中最大的行内框的值

如果要处理大量文本, 建议把行高设置比字体大, 因为当行高比内容区小时, 上下缺少的高度渲染会根据不同浏览器进行变化

格式化css时一般会统一设置line-height为1, 即font-size的一倍, 纯数字为行高因子 行高因子继承时, 是行高因子, 其他的百分比, 像素等是继承计算值

垂直居中
  • vertical-align

    用来指定inline-block的垂直

    当给图片垂直居中时可以通过伪元素after设置一个内联块状元素来使与图片同一行的行内框撑满父元素

    再给图片设置vertical-align: middle就能使图片垂直居中

浮动

浮动float中已经详细介绍了浮动的概念,在此处做一些应用浮动来布局的补充

开启浮动时元素层数可以理解为一个元素占两层, 第一层为盒模型, 第二层为文字, 且只有盒模型提升了半层, 此理论只在浮动有效, 当使用其他语句改变元素层级时仍然看成只有一层

清除浮动
  1. 给父元素设置高度, 扩展性差

    给父元素设置成BFC, 在IE6,7中用开启haslayout解决

  2. 给父元素设置overflow:hidden, 移动端常用
  3. 给父元素设置定位
  4. 给父元素设置浮动

    定位盒子或者浮动盒子的宽高会默认由内容撑开而其余的宽高默认为包含块的100%

  5. 空div标签清除浮动, 在IE6中可能会造成最小高度问题且违反结构行为样式分离原则
    • 当给div设置高度时, 在IE6中最小只能设置成font-size的最小值12px, 但是即使将font-size设置成0时也依然会有2px的高度, 继续设置overflow: hidden才能解决
  6. br标签用html属性clear来清除浮动,在IE6中无效且违反结构行为样式分离原则
  7. 伪元素清除浮动, 在IE6,7中不支持, 但使用zoom可解决, PC端常用
包含块

一个元素的尺寸和位置经常受其包含块的影响。大多数情况下,包含块就是这个元素最近的祖先块元素的内容区,但也不是总是这样

在大多数浏览器, 初始包含块是一个视窗大小的矩形

  • 对于浮动元素, 包含块是最近的块级祖先元素的内容边界构成
  • 对于非根元素, positionrelativestatic, 包含块由最近的块级框的内容边界构成
  • 对于非根元素, positionabsolute, 包含块为最近的position不是static的祖先元素

    如果祖先元素为块级元素, 包含块则为该元素的内边距边界

    如果没有祖先, 元素包含块为初始包含块

left,top,bottom,right,width,height默认值为auto, 不可继承

margin, padding默认值为0, 不可继承

border-width默认值为medium

width, left, padding, margin的百分比是包含块的width

height, top的百分比是包含块的height

html与body的高度问题
  • 需要视图, html, body高度三合一时后两者都必须设置height:100%, 没有设置html高度时, body高度都由内容撑开
  • html或者body其中之一使用overflow:scroll时, 都是让document出现滚动条
  • htmlbody都设置时, htmlscroll设置给document, body的才设置给body
  • 常被用来禁用系统默认滚动条, 代码为:

      html, body{
          height: 100%;
          overflow: hidden;
      }
    
  • 当使用此代码后再使body具有overflow:scroll使body滚动条模拟视图滚动条时, 可以使用绝对定位模拟固定定位, 常用于移动端或解决IE6没有fixed的问题
兼容性简述
  • 可以发现在CSS的知识点中反复提及了兼容的方案,而浏览器兼容问题是css中比较庞大的知识,随着时代进步老旧浏览器市场份额的下降一些兼容代码也可以抛弃了,但是一些基本的兼容思想仍然需要具有

  • 检测低版本IE

    兼容中最头疼在于兼容IE, css中可以直接使用hack来做到,但是在某些情况下需要使用js控制表现时要关注兼容性问题

    js中结合b标签的innerHTML与条件hack, 最后判断innerHTML内的标签是否存在来决定版本

      function isIE(version){
          var b = document.createElement("b");
          b.innerHTML = "<!--[if IE "+version+"]><i></i><![endif]-->"
          return b.getElementByTagname("i").length == 1;
      }
    

    可以使用caniuse.com网站来检查api兼容性

常见布局

  • 实现可用的三列布局具有以下要求

    1. 两边固定, 当中自适应
    2. 中间要完全显示
    3. 当中列要优先加载
    • 绝对定位来三列布局
      • 中间不能设置最小宽度, 不推荐
    • 浮动来三列布局
      • 中间页面会最后加载
    • 圣杯布局
      • 使用浮动搭建基本布局
      • 调整左右的margin-left使其处于同一行
      • center使用padding向内预留两栏的位置模仿圣杯的形状, 同时设置box-sizingborder-box使其总宽度不变
      • 代码实现
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta http-equiv="X-UA-Compatible" content="ie=edge">
            <title>Document</title>
            <style>
                *{
                    margin: 0;
                    padding: 0;
                }
                .clearfix:after{
                    content: "";
                    display: block;
                    clear: both;
                }
                .content{
                    min-width: 600px;
                    background-color: #ccc;
                }
                .center{
                    width: 100%;
                    height: 800px;
                    background-color: #aaa;
                    float: left;
                    padding: 0 200px;
                    box-sizing: border-box;
                }
                .left{
                    width: 200px;
                    height: 800px;
                    margin-left: -100%;
                    background-color: #acf;
                    float: left;
                }
                .right{
                    width: 200px;
                    height: 800px;
                    margin-left: -200px;
                    background-color: #acf;
                    float: left;
                }
            </style>
        </head>
        <body>
            <div class="content clearfix">
                <div class="center">
                </div>
                <div class="left"></div>
                <div class="right"></div>
            </div>
        </body>
        </html>
      
伪等高布局
  • 给三列内容设置非常大的下内边距
  • 给三列内容设置与下内边距相同的下外边距, 为负值, 使外部块元素大小与未设置下内边距时相同
  • 给外部块元素设置溢出隐藏

代码实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>sameheight</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .content{
            width: 900px;
            border: 1px solid #acf;
            overflow: hidden;
        }
        .clearfix:after{
            content: "";
            display: block;
            clear: both;
        }
        .left,.right{
            float: left;
            width: 400px;
            background-color: #ccc;
            padding-bottom: 1000px;
            margin-bottom: -1000px;
            border: 1px dashed #aaa;

        }
    </style>
</head>
<body>
    <div class="content clearfix">
        <div class="left">
            a <br>
            a <br>
            a <br>
            a <br>
            a <br>
            a <br>
            a <br>
            a <br>
            a <br>
        </div>
        <div class="right">
                a <br>
                a <br>
                a <br>
                a <br>
                a <br>
        </div>
    </div>
</body>
</html>
双飞翼布局
  • 与圣杯布局基本类似, 但是此处使用margin来模仿飞翼达到圣杯布局中填充的效果
  • center内中用一个块元素来充当真正存放内容的元素, 并给其与左右两栏相同宽度的margin

代码实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .clearfix:after{
            content: "";
            display: block;
            clear: both;
        }
        .content{
            min-width: 600px;
            background-color: #ccc;
        }
        .center{
            width: 100%;
            height: 800px;
            background-color: #aaa;
            float: left;
        }
        .inner-center{
            margin: 0 200px;
        }
        .left{
            width: 200px;
            height: 800px;
            margin-left: -100%;
            background-color: #acf;
            float: left;
        }
        .right{
            width: 200px;
            height: 800px;
            margin-left: -200px;
            background-color: #acf;
            float: left;
        }
    </style>
</head>
<body>
    <div class="content clearfix">
        <div class="center">
            <div class="inner-center"></div>
        </div>
        <div class="left"></div>
        <div class="right"></div>
    </div>
</body>
</html>

粘连布局

  • 有一块内容<main>
  • <main>的高度足够长时, <footer>跟在<main>的后面
  • <main>元素比较短的时候, 我们希望<footer>能够“粘连”在底部 1.给wrap设置最小高度 2.给main设置相等于footer高度的下内边距, 为了不让footer遮挡住main里的文字,因为main高度是百分比所以视图中没有给footer占位的地方 3.给footer设置相等于自身高度的上外边距, 值为负
    <!--html布局-->
    <div class="wrap">
        <div class="main"></div>
    </div>
    <div class="footer"></div>

代码实现

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>粘连布局</title>
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        html,body{
            height: 100%;
        }
        .content{
            min-height: 100%;
            background-color: #ccc;
            padding-bottom: 200px;
            box-sizing: border-box;
        }
        .footer{
            height: 200px;
            background-color: #acf;
            margin-top: -200px;
        }
    </style>
</head>
<body>
    <div class="content">
        <div class="main">
            a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>>a<br/>a<br/>a<br/>a<br/>>a<br/>a<br/>a<br/>a<br/>>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>a<br/>
        </div>
    </div>
    <div class="footer"></div>
</body>
</html>

其他常用的知识

  • 有很多时候需要将一段文字超出块元素的部分以省略号代替

实现代码

.text-overflow{
    white-spacing: nowrap; /*强制不换行*/
    text-overflow: ellipsis;  /*决定如何对溢出文本应用样式*/
    overflow: hidden;
    display: block;     /*保持元素长宽固定,
                         如display为inline-block宽度会被内容撑开, 
                         使多余部分不会出现省略号*/
}