7. CSS揭秘——结构和布局

1. 自适应内部元素的宽度max-width: min-content;

如果不给元素指定一个具体的 height,它就会自动适应其内容的高度。尝试对width 也实现类似的行为。

使 figure 元素能跟它所包含的图片一样宽(图片的尺寸往往不是固定的),而且是水平居中的。

  • <figure> 元素浮动会让它得到正确的宽度,但同时也彻底改变了它的布局模式
  • 对 figure 应用 display: inline-block 会让它根据内容来决定自身的尺寸,但很难继续完成水平居中
  • 对 fifigure 应用一个固定的 width 或max-width ,然后对 figure > img 应用 max-width: 100%。可是这个方法无法实现响应式

width 和 height 属性有一个新的关键字 min-content 。这个关键字将解析为这个容器内部最大的不可断行元素的宽度(即最宽的单词、图片或具有固定宽度的盒元素)。

figure {
    width: min-content;
    margin: auto;
}
<!DOCTYPE html>
<html>
  <head>
    <style>
      figure {
        /*回退机制*/
        /* max-width: 300px;  */
        /*把 figure 设置为恰当的宽度,并让它水平居中*/
        max-width: min-content;
        margin: auto;
      }

      figure > img {
        max-width: inherit;
      }
      /* Basic styling */
      figure {
        padding: 10px;
        border: 1px solid silver;
      }
    </style>
  </head>
  <body>
    <p>
      Let’s assume we have some text here. Bacon ipsum dolor sit amet turkey
      veniam shankle, culpa short ribs kevin t-bone occaecat.
    </p>
    <figure>
      <img src="http://csssecrets.io/images/adamcatlace.jpg" />
      <figcaption>
        The great Sir Adam Catlace was named after Countess Ada Lovelace, the
        first programmer ever.
      </figcaption>
    </figure>
    <p>
      We also have some more text here. Et laborum venison nostrud, ut veniam
      sint kielbasa ullamco pancetta.
    </p>
  </body>
</html>

1.gif

2. 精确控制表格列宽——table-layout: fixed

table-layout的默认值是 auto,其行为模式是我们最为熟悉的表格布局行为

对表格应用 table-layout: fixed 之后的效果。按从上到下的顺序总结为:

  • 如果不指定任何宽度,则各列的宽度将是平均分配的;
  • 后续的表格行并不会影响列宽;
  • 给单元格指定很大的宽度也会直接生效,并不会自动缩小;
  • overflow 和 text-overflow属性都是可以正常生效的;
  • 如果overflow 的值是 visible,则单元格的内容有可能会溢出

<table> 元素或其他具有 display: table 样式的元素应用table-layout: fixed属性

请注意,为了确保table-layout: fixed奏效,需要为这些表格元素指定一个宽度(哪怕是 100%)。同样,为了让 text-overflow:ellipsis 发挥作用,还需要为那一列指定宽度

<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        background: #ddd;
      }

      section {
        width: 500px;
        margin: 2em;
        background: white;
      }

      table {
        border-collapse: collapse;
        margin-bottom: 1em;
        width: 100%;
      }

      section + section table {
        table-layout: fixed;
      }

      td {
        border: 1px solid #aaa;
      }

      td.preformatted {
        white-space: pre;
        font-family: Consolas, Monaco, monospace;
        text-overflow: ellipsis;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <section>
      <h1>With table-layout: auto</h1>
      <div>
        <table>
          <tr>
            <td>If we don’t…</td>
            <td>
              specify a cell width, they will be assigned one that depends on
              their contents. Notice how the cell with the more content here is
              much wider.
            </td>
          </tr>
        </table>

        <table>
          <tr>
            <td>If we don’t…</td>
            <td>
              specify a cell width, they will be assigned one that depends on
              their contents. Notice how the cell with the more content here is
              much wider.
            </td>
          </tr>
          <tr>
            <td>
              All rows take part in calculating the widths, not just the first
              one.
            </td>
            <td>
              Notice how the dimensions here are different than the previous
              example.
            </td>
          </tr>
        </table>

        <table>
          <tr>
            <td style="width: 1000px">
              If we specify a width, it will not always be followed. I have a
              width of <code>1000px</code>…
            </td>
            <td style="width: 2000px">
              …and I have a width of <code>2000px</code>. Because there’s not
              enough space for <code>3000px</code>, they are reduced
              proportionally, to 33.3% and 66.6% of the total width.
            </td>
          </tr>
        </table>

        <table>
          <tr>
            <td>
              If we prevent word wrapping, the table can become so wide it grows
              beyond its container.
            </td>
            <td class="preformatted">
              …and <code>text-overflow: ellipsis</code> doesn’t help either.
            </td>
          </tr>
        </table>

        <table>
          <tr>
            <td>
              Large images and blocks of code can also cause the same issue.
            </td>
            <td><img src="http://lea.verou.me/book/panoramic.jpg" /></td>
          </tr>
        </table>
      </div>
    </section>

    <section>
      <h1>With table-layout: fixed</h1>
      <div></div>
    </section>
  </body>
  <script>
    document.querySelector(
      "section + section div"
    ).innerHTML = document.querySelector("section:first-of-type div").innerHTML;
  </script>
</html>

1.gif

3. 根据兄弟元素的数量来设置样式

当一个列表不断延长时,通过隐藏控件或压缩控件等方式来节省屏幕空间,以此提升用户体验

li:first-child:nth-last-child(1) {
   /* 相当于li:only-child */
}

用兄弟选择符(~)来命中之后的所有兄弟元素

li:first-child:nth-last-child(4)CSS选择器——找一个同时匹配 :first-child:nth-last-child(4) 的元素,这个元素需要是父元素的第一个子元素,同时还需要是从后往前数的第四个子元素

li:first-child:nth-last-child(4),
li:first-child:nth-last-child(4) ~ li {
  /* 当列表正好包含四项时,命中所有列表项 */
}
/* 定义mixin */
@mixin n-items($n) {
  &:first-child:nth-last-child(#{$n}),
  &:first-child:nth-last-child(#{$n}) ~ & {
    @content;
  }
}
/* 调用时是这样的: */
li {
  @include n-items(4) {
    /* 属性与值写在这里 */
  }
}

3.1 根据兄弟元素的数量范围来匹配元素

  • :nth-child(n+b) 这种形式的表达式可以选中从第 b 个开始的所有子元素
  • :nth-child(-n+b) 这种形式的表达式可以选中开头的 b 个元素

:nth-child(n+4) 将会选中除了第一、二、三个子元素之外的所有子元素

li:first-child:nth-last-child(n+4),
li:first-child:nth-last-child(n+4) ~ li {
  /* 当列表至少包含四项时,命中所有列表项 */
}
li:first-child:nth-last-child(-n+4),
li:first-child:nth-last-child(-n+4) ~ li {
/* 当列表最多包含四项时,命中所有列表项 */
}
li:first-child:nth-last-child(n+2):nth-last-child(-n+6),
li:first-child:nth-last-child(n+2):nth-last-child(-n+6) ~ li {
/* 当列表包含2~6项时,命中所有列表项 */
}

1.gif

<!DOCTYPE html>
<html>
  <head>
    <style>
      /* Hide "color" 4 items or more */
      .palette li:first-child:nth-last-child(n + 4) .color-options a:after,
      .palette
        li:first-child:nth-last-child(n + 4)
        ~ li
        .color-options
        a:after {
        content: none;
      }

      /* Hide word when 6 items or more */
      .palette li:first-child:nth-last-child(n + 6) .color-options a,
      .palette li:first-child:nth-last-child(n + 6) ~ li .color-options a {
        color: transparent;
        font-size: 0;
      }

      .palette li:only-child .delete {
        display: none;
      }

      /* From this point it’s just styling */
      .palette {
        display: flex;
        height: 200px;
        max-width: 900px;
        font: bold 90%/1 sans-serif;
      }

      .palette li {
        flex: 1;
        list-style: none;
        background: #d6e055;
      }

      .color-options {
        background: rgba(0, 0, 0, 0.5);
        padding: 10px;
        margin: 0 10px;
        overflow: hidden;
        border-radius: 0 0 10px 10px;
      }

      .color-options .add {
        float: left;
      }
      .color-options .delete {
        float: right;
      }

      .color-options a {
        color: white;
        text-decoration: none;
      }

      .color-options a:before {
        display: inline-block;
        font-size: 1rem;
        width: 1.3rem;
        margin-right: 0.3rem;
        text-align: center;
        line-height: 1.3;
        background: white;
        border-radius: 50%;
        letter-spacing: normal;
      }

      .color-options .add:before {
        content: "✚";
        color: #590;
      }

      .color-options .delete:before {
        content: "✖";
        color: #b00;
      }

      .color-options a:after {
        content: " color";
        font-weight: normal;
      }
    </style>
  </head>
  <body>
    <ul class="palette">
      <li>
        <div class="color-options">
          <a class="add" href="#">Add</a>
          <a class="delete" href="#">Delete</a>
        </div>
      </li>
    </ul>
  </body>
  <script>
    function $$(expr, con) {
      return [].slice.call((con || document).querySelectorAll(expr));
    }

    var colors = [
        "#D6E055", // Agave
        "#082323",
        "#E6E2AF",
        "#A7A37E",
        "#EFECCA",
        "#046380", // Sandy stone beach
        "#1C171D",
        "#FEE169",
        "#CDD452",
        "#F9722E",
        "#C9313D", // Sushi Maki
        "#2E95A3",
        "#50B8B4",
        "#C6FFFA",
        "#E2FFA8" // Agave
      ],
      palette = document.querySelector(".palette"),
      template = palette.firstElementChild;

    function addColor(template) {
      var li = template.cloneNode(true);
      var color = colors.pop();
      colors.unshift(color);
      li.style.background = color;
      palette.insertBefore(li, template.nextSibling);
    }

    palette.onclick = function(evt) {
      var button = evt.target;

      if (button.className == "add") {
        addColor(button.parentNode.parentNode);
      } else if (button.className == "delete") {
        var li = button.parentNode.parentNode;
        li.parentNode.removeChild(li);
      }
    };
  </script>
</html>

4. 满幅的背景,定宽的内容

最常见的方法就是添加一层额外的HTML元素:外层用来实现满幅的背景,内层用来实现定宽的内容。后者是通过margin: auto实现水平居中的。

<footer>
  <div class="wrapper">
    <!-- 页脚的内容写在这里 -->
  </div>
</footer>
footer {
  background: #333;
}
.wrapper {
  max-width: 900px;
  margin: 1em auto;
}

4.1 用 calc() 替代 margin: auto

不需要添加一层额外的元素,把这个calc()长度值应用到父元素的 padding 上

<!DOCTYPE html>
<html>
  <head>
    <style>
      header,
      section,
      footer {
        padding: 1em calc(50% - 350px);
      }

      footer {
        background: #333;
        color: white;
      }

      header {
        background: orange;
        color: white;
      }

      section + section {
        background: #eee;
      }

      body {
        margin: 0;
        font: 100%/1.5 sans-serif;
      }
    </style>
  </head>
  <body>
    <header>
      <h1>Fluid background, <br />fixed content</h1>
    </header>
    <section>
      <h1>Heading</h1>
      <p>
        Bacon ipsum dolor amet voluptate et shoulder, ipsum flank tongue
        exercitation commodo sed beef ribs drumstick in venison laborum. Laboris
        ut enim id drumstick, et aute esse. Consequat ad kielbasa anim pork loin
        turkey qui cupidatat drumstick doner labore. Nulla sirloin jerky do sed
        magna meatloaf. Ribeye ea ut elit leberkas laboris sausage corned beef
        drumstick cillum non.
      </p>
    </section>
    <section>
      <h1>Another heading</h1>
      <p>
        Nostrud landjaeger cillum beef cow tail cupidatat non mollit voluptate
        jowl. Enim sunt in, flank hamburger proident qui. Id aute excepteur
        chuck magna tempor ipsum pork chop t-bone. Frankfurter meatball pork
        loin beef et leberkas pork. Pig ball tip pancetta in.
      </p>
      <p>
        Ribeye in veniam ipsum flank. Elit incididunt t-bone proident meatball.
        Porchetta exercitation prosciutto sausage chuck ut eu brisket shank
        pastrami turkey sunt laboris tenderloin anim. Landjaeger do venison
        laboris kevin.
      </p>
    </section>
    <footer>
      <p>&copy; 2015 Lea Verou (j/k, feel free to use wherever)</p>
      <p>
        Consectetur et t-bone pork loin. Tri-tip cupim in, spare ribs velit
        exercitation in. Tempor cillum fugiat, nisi leberkas reprehenderit anim
        laboris proident cow. Eu fatback kevin sint, ad shoulder in venison
        picanha. Sausage drumstick capicola, culpa boudin pork belly minim aute
        ipsum biltong picanha venison nulla adipisicing.
      </p>
    </footer>
  </body>
</html>

1.gif

5. 垂直居中

在 CSS 中对元素进行水平居中是非常简单的:

  • 如果它是一个行内元素,就对它的父元素应用 text-align: center
  • 如果它是一个块级元素,就对它自身应用 margin: auto

5.1 基于绝对定位的方案

<!DOCTYPE html>
<html>
  <head>
    <style>
      main {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%); /*解除对固定尺寸的依赖*/

        padding: 1em 1.5em;
        box-sizing: border-box;
        background: #655;
        color: white;
        text-align: center;
      }

      h1 {
        margin: 0 0 0.2em;
        font-size: 150%;
      }

      p {
        margin: 0;
      }

      body {
        background: #fb3;
        font: 100%/1.5 sans-serif;
      }
    </style>
  </head>
  <body>
    <main>
      <h1>Am I centered yet?</h1>
      <p>Center me, please!</p>
    </main>
  </body>
</html>

1.gif

5.2 基于视口单位的解决方案 margin: 50vh auto 0;

不使用绝对定位,仍然可以采用 translate() 技巧来把这个元素以其自身宽高的一半为距离进行移动

main {
    width: 18em;
    padding: 1em 1.5em;
    margin: 50vh auto 0;
    transform: translateY(-50%);
    box-sizing: border-box;
    background: #655;
    color: white;
    text-align: center;
}

5.3 基于 Flexbox 的解决方案

先给这个待居中元素的父元素设置 display:flex,再给这个元素自身设置 margin: auto

注意,当我们使用 Flexbox 时,margin: auto 不仅在水平方向上将元素居中,垂直方向上也是如此。

还可以将匿名容器(即没有被标签包裹的文本节点)垂直居中

<main>Center me, please!</main>
main {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18em;
    height: 10em;
}
<!DOCTYPE html>
<html>
  <head>
    <style>
      * {
        margin: 0;
      }

      body {
        display: flex;
        min-height: 100vh;
      }

      main {
        padding: 1em 2em;
        margin: auto;
        box-sizing: border-box;
        background: #655;
        color: white;
        text-align: center;
      }

      h1 {
        margin-bottom: 0.2em;
        font-size: 150%;
      }

      body {
        background: #fb3;
        font: 100%/1.5 sans-serif;
      }
    </style>
  </head>
  <body>
    <main>
      <h1>Am I centered yet?</h1>
      <p>Center me, please!</p>
    </main>
  </body>
</html>

1.gif

5.4 align-self: center;

image.png

image.png

6. 紧贴底部的页脚

6.1 固定高度的解决方案

这个方案不仅要求我们确保页脚内的文本永远不会折行,而且每当我们改变页脚的尺寸时,都需要跟着调整min-height 值

<!DOCTYPE html>
<html>
  <head>
    <style>
      main {
        min-height: calc(100vh - 5em - 7em);
      }

      /* Toggle checkbox to alternate between short/long content */
      #contents:checked ~ p {
        display: none;
      }

      /* Basic styling */
      body {
        margin: 0;
        font: 100%/1.5 Palatino Linotype, Palatino, serif;
      }

      h1 {
        margin: 0.5em 0 0;
      }

      header {
        text-align: center;
        height: 3em;
      }

      main,
      footer {
        display: block;
        padding: 0.5em calc(50% - 400px);
      }

      footer {
        background: linear-gradient(#222, #444);
        color: white;
        height: 6em;
      }
    </style>
  </head>
  <body>
    <header>
      <h1>Site name</h1>
    </header>
    <main>
      <input type="checkbox" id="contents" /><label for="contents"
        >Toggle contents</label
      >
      <p>
        Bacon ipsum dolor sit amet turkey veniam shankle, culpa short ribs kevin
        t-bone occaecat. Et laborum venison nostrud, ut veniam sint kielbasa
        ullamco pancetta. Qui drumstick tail, bacon leberkas shoulder capicola
        laborum. Minim ipsum bacon, mollit laboris t-bone pariatur. Ham hock
        reprehenderit sint beef, sausage pig eiusmod t-bone shankle strip steak.
      </p>
      <p>
        Cow enim excepteur, boudin dolore lorem magna fugiat consequat
        voluptate. Picanha fugiat chicken, cupim aliquip magna filet mignon
        prosciutto ut nostrud. Kielbasa rump frankfurter sunt corned beef.
        Andouille in cillum deserunt, rump et picanha landjaeger tongue anim.
      </p>
      <p>
        Ad meatball ipsum ground round shank. Quis ipsum meatball exercitation.
        Laborum swine spare ribs, sunt ball tip magna t-bone venison. Velit
        doner voluptate non occaecat do ribeye kevin strip steak et. Esse
        biltong shank ribeye dolor pariatur aute deserunt non est eiusmod pork
        belly pancetta pork chop. Pork chop id consectetur rump, meatball short
        loin brisket tail andouille deserunt alcatra irure prosciutto do.
      </p>
      <p>
        Dolore reprehenderit ex, meatball doner commodo consectetur ea ribeye.
        Ad aliqua kevin, chuck excepteur minim et cow esse ham hock landjaeger.
        Alcatra bresaola dolore tempor do, excepteur in velit flank officia
        dolore meatloaf corned beef picanha. Eu pancetta brisket eiusmod ipsum
        aute sausage, culpa rump shoulder excepteur nostrud venison sed pork
        loin. Tempor proident do magna ground round. Ut venison frankfurter et
        veniam dolore. Pig pork belly beef ribs kevin, doner exercitation magna
        esse shankle.
      </p>
      <p>
        Flank anim chuck boudin id consectetur bresaola ham pork loin cupim
        andouille frankfurter. Proident do ball tip nostrud nulla sed,
        frankfurter ut commodo corned beef ut. Ex aute in, pig deserunt beef
        ribs turducken pastrami irure ball tip pork belly pork chop sausage id.
        Chicken sunt nisi tempor sed. In eiusmod non fatback tempor tenderloin
        pastrami adipisicing cow lorem ut tail jerky cupidatat venison. Jowl
        consequat commodo pork loin ipsum pork belly prosciutto aute beef. Ball
        tip shoulder aliqua, fugiat landjaeger kevin pork chop beef ribs
        leberkas hamburger cillum turkey ut doner culpa.
      </p>
    </main>
    <footer>
      <p>© 2015 No rights reserved.</p>
      <p>Made with ♥ by an anonymous pastafarian.</p>
    </footer>
  </body>
</html>

1.gif

6.2 Flexbox方案

  • 首先,对 <body> 元素设置 display: flex,因为它是这三个主要区块的父元素,
  • 当父元素获得这个属性之后,就可以对其子元素触发“伸缩盒布局模型”。
  • 我们还需要把 flex-flow 设置为 column,否则子元素会被水平排放在一行上

<body> 的 min-height 属性指定为 100vh,这样它就至少会占据整个视口的高度
而内容区块的高度应该可以自动伸展并占满所有的可用空间。我们只要给 <main>这个容器的 flflex 属性指定一个大于 0 的值(比如 1 即可)

body {
    display: flex;
    flex-flow: column;
    min-height: 100vh;
}
main {
    flex: 1;
}
© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容