1. CSS揭秘——引言

129 阅读6分钟

1. 前期准备工作

1.1 一个工具函数——$$()——获取所有匹配特定 CSS 选择器的 DOM 元素

function $$(selector, context) {
  context = context || document;
  var elements = context.querySelectorAll(selector);
  return Array.prototype.slice.call(elements);
}

image.png

1.2 浏览器兼容性信息

有很多优秀的网站提供了及时有效的浏览器兼容性信息。推荐如下:

1.3 检测某个CSS样式属性是否被浏览器支持

如果要检测某个样式属性是否被支持,核心思路就是在任一元素的 element.style 对象上检查该属性是否存在

function testProperty(property) {
  var root = document.documentElement; // html
  if (property in root.style) {
    root.classList.add(property.toLowerCase());
    return true;
  }
  root.classList.add("no-" + property.toLowerCase());
  return false;
}

1.gif

1.4 检测一个CSS样式属性是否支持设置某个具体的属性值

如果我们想要检测某个具体的属性值是否支持,那就需要把它赋给对应的属性,然后再检查浏览器是不是还保存着这个值。很显然,这个过程会改变元素的样式,因此我们需要一个隐藏元素:

function testValue(id, value, property) {
  var dummy = document.createElement("p");
  var root = document.documentElement;
  dummy.style[property] = value;
  if (dummy.style[property]) {
    root.classList.add(id);
    return true;
  }
  root.classList.add("no-" + id);
  return false;
}

1.gif

2. CSS编码技巧

2.1 尽量减少代码重复

在实践中,代码可维护性的最大要素是尽量减少改动时要编辑的地方。

这还不仅仅是后期修改的问题。灵活的 CSS 通常更容易扩展:在写出基础样式之后,只用极少的代码就可以扩展出不同的变体,因为只需覆盖一些变量就可以了。看一个例子:

button {
  padding: 6px 16px;
  border: 1px solid #446d88;
  background: #58a linear-gradient(#77a0bb, #58a);
  border-radius: 4px;
  box-shadow: 0 1px 5px gray;
  color: white;
  text-shadow: 0 -1px 1px #335166;
  font-size: 20px;
  line-height: 30px;
}

image.png

在这个例子中,行高是字号的 1.5 倍。因此,把代码改成下面这样会更易维护:

font-size: 20px;
line-height: 1.5;

但是,把父级的字号加大之后,就得修改每一处使用绝对值作为字体大小的样式。如果改用百分比或 em 单位就好多了:

font-size: 125%; /* 假设父级的字号是 16px */
line-height: 1.5;

如果把所有长度值都改成 em 单位,那这些效果的值就都变成可缩放的了,而且是依赖字号进行缩放

em 单位可以看成是 CSS 中的一个变量,因为它引用了 font-size 的值

button {
    padding: 0.3em 0.8em;
    border: 1px solid #446d88;
    background: #58a linear-gradient(#77a0bb, #58a);
    border-radius: 0.2em;
    box-shadow: 0 0.05em 0.25em gray;
    color: white;
    text-shadow: 0 -0.05em 0.05em #335166;
    font-size: 125%;
    line-height: 1.5;
}

1.gif

按钮的边框粗细保持在 1px,不受按钮尺寸的影响。

把半透明的黑色或白色叠加在主色调上,即可产生主色调的亮色和暗色变体,这样就能导出其他颜色各自的亮色和暗色版本:

button {
    padding: 0.3em 0.8em;
    border: 1px solid rgba(0, 0, 0, 0.1);
    background: #58a linear-gradient(hsla(0, 0%, 100%, 0.2), transparent);
    border-radius: 0.2em;
    box-shadow: 0 0.05em 0.25em rgba(0, 0, 0, 0.5);
    color: white;
    text-shadow: 0 -0.05em 0.05em rgba(0, 0, 0, 0.5);
    font-size: 125%;
    line-height: 1.5;
}

覆盖 background-color 属性,就可以得到不同颜色版本的按钮了

<!DOCTYPE html>
<html>
  <head>
    <style>
      button {
        padding: 0.3em 0.8em;
        border: 1px solid rgba(0, 0, 0, 0.1);
        background: #58a linear-gradient(hsla(0, 0%, 100%, 0.2), transparent);
        border-radius: 0.2em;
        box-shadow: 0 0.05em 0.25em rgba(0, 0, 0, 0.5);
        color: white;
        text-shadow: 0 -0.05em 0.05em rgba(0, 0, 0, 0.5);
        font-size: 125%;
        line-height: 1.5;
      }
      button.cancel {
        background-color: #c00;
      }
      button.ok {
        background-color: #6b0;
      }
    </style>
  </head>

  <body>
    <button>Yes!</button>
    <button class="ok">ok!</button>
    <button class="cancel">cancel!</button>
  </body>
</html>

1.gif

2.1.1. 代码易维护 vs. 代码量少

有时候,代码易维护和代码量少不可兼得。比如在上面的例子中,最终采用的代码甚至比一开始的版本略长。

下面的代码片段,为一个元素添加一道 10px 宽的边框,但左侧不加边框。

border-width: 10px 10px 10px 0;

只要这一条声明就可以搞定了,但如果日后要改动边框的宽度,你需要同时改三个地方。

如果把它拆成两条声明的话,改起来就容易多了,而且可读性或许更好一些:

border-width: 10px;
border-left-width: 0;

2.1.2 currentColor——CSS变量

currentColor是从SVG 那里借鉴来的。这个关键字并没有绑定到一个固定的颜色值,而是一 直被解析为 color。

举个例子,假设想让所有的水平分割线(所有 <hr> 元素)自动与文本的颜色保持一致

hr {
    height: 0.5em;
    background: currentColor;
 }

1.gif

currentColor 是很多 CSS 颜色属性的初始值,比如border-color 和 outline-color,以及 text-shadow 和 box-shadow 的颜色值,等等。

2.1.3 inherit继承

  • inherit 可以用在任何 CSS 属性中,而且它总是绑定到父元素的计算值
  • 对伪元素来说,inherit则会取生成该伪元素的宿主元素。

举例来说,要把超链接的颜色设定为与页面中其他文本相同,还是要用 inherit:

a { color: inherit; }

1.gif

inherit 关键字对于背景色同样非常有用。举个例子,在创建提 示框的时候,希望它的小箭头能够自动继承背景和边框的样式:

  .callout {
    position: relative;
    background-color: yellowgreen;
    border: 1px solid red;
    height: 50px;
    width: 300px;
  }

  .callout::before {
    content: "";
    position: absolute;
    top: -0.4em;
    left: 1em;
    padding: 0.35em;
    background: inherit;
    border: inherit;
    border-right: 0;
    border-bottom: 0;
    transform: rotate(45deg);
  }

image.png

2.2 相信你的眼睛,而不是数字

人的眼睛并不是一台完美的输入设备。有时候精准的尺度看起来并不精准,而我们的设计需要顺应这种偏差。

2.3 响应式布局——避免不必要的媒体查询

总的来说,我们的思路是尽最大努力实现弹性可伸缩的布局,并在媒体查询的各个断点区间内指定相应的尺寸。当网页本身的设计足够灵活时,让它变成响应式应该只需要用到一些简短的媒体查询代码。

  • 使用百分比长度来取代固定长度。如果实在做不到这一点,也应该尝试使用与视口相关的单位(vw、vh、vmin 和 vmax),它们的值解析为视口宽度或高度的百分比。
  • 在较大分辨率下得到固定宽度时,使用 max-width 而不是width,因为它可以适应较小的分辨率,而无需使用媒体查询。
  • 为替换元素(比如 img、object、video、iframe 等)设置一个 max-width,值为 100%。
  • 假如背景图片需要完整地铺满一个容器,不管容器的尺寸如何变化,background-size: cover 这个属性都可以做到。但是,在移动网页中通过 CSS 把一张大图缩小显示往往是不太明智的。
  • 当图片(或其他元素)以行列式进行布局时,让视口的宽度来决定列的数量。弹性盒布局(即 Flexbox)或者 display: inline-block加上常规的文本折行行为,都可以实现这一点。
  • 在 使 用 多 列 文 本 时, 指 定 column-width( 列 宽 ) 而 不 是 指 定column-count(列数),这样它就可以在较小的屏幕上自动显示为单列布局

2.4 合理使用CSS简写属性

background: url(tr.png) no-repeat top right / 2em 2em,
          url(br.png) no-repeat bottom right / 2em 2em,
          url(bl.png) no-repeat bottom left / 2em 2em;

可以把重复的值从简写属性中抽出来写成一个展开式属性:

background: url(tr.png) top right, url(br.png) bottom right,
          url(bl.png) bottom left;
background-size: 2em 2em;
background-repeat: no-repeat;

2.5 使用CSS预处理器

Stylus(stylus-lang.com/)、Sass(http… LESS(lesscss.org/)等CSS 预处理器,为 CSS 的编写提供提供了,变量、mixin、函数、规则嵌套、颜色处理等